You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
chore(workflow): integrate pytest-beehave and clean up @id conventions (#78)
* chore(workflow): integrate pytest-beehave and clean up @id conventions
- Add pytest-beehave[html]>=3.0 to dev deps; configure [tool.beehave]
- Fix test-build task: --cov=pytest_beehave -> --cov=app
- Remove manual deprecated skip hook from conftest.py (beehave owns it)
- Naming: feature-stem for .feature paths, feature_slug for test dirs
- @id tags now auto-assigned on first pytest run; remove all manual generation instructions
- Test stubs auto-generated at Step 2 end via test-fast; remove manual stub section
- Add pytest-beehave to README tooling table; add Why section and auto-gen stub example
- Update product-owner.md, AGENTS.md, and all affected skills accordingly
* chore(workflow): number self-declaration items and add completeness enforcement
- implementation/SKILL.md: number all 25 items 1-25; add count reminder comment
- verify/SKILL.md: add completeness hard gate (count must be 25, sequence must be gapless); expand report table from 21 to 25 numbered rows matching implementation template exactly
Copy file name to clipboardExpand all lines: .opencode/agents/product-owner.md
+6-7Lines changed: 6 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -51,19 +51,18 @@ When a gap is reported (by software-engineer or reviewer):
51
51
52
52
| Situation | Action |
53
53
|---|---|
54
-
| Edge case within current user stories | Add a new Example with a new `@id`to the relevant `.feature` file. |
54
+
| Edge case within current user stories | Add a new Example to the relevant `.feature` file. |
55
55
| New behavior beyond current stories | Add to backlog as a new feature. Do not extend the current feature. |
56
-
| Behavior contradicts an existing Example |Write a new Example with new `@id`. |
57
-
| Post-merge defect | Move the `.feature` file back to `in-progress/`, add new Example with `@id`, resume at Step 3. |
56
+
| Behavior contradicts an existing Example |Add `@deprecated` to the old Example; write a new Example. |
57
+
| Post-merge defect | Move the `.feature` file back to `in-progress/`, add new Example, resume at Step 3. |
58
58
59
59
## Bug Handling
60
60
61
61
When a defect is reported against any feature:
62
62
63
-
1. Add a `@bug @id:<new-8-char-hex>` Example to the relevant `Rule:` block in the `.feature` file.
64
-
2. Write the Example using the standard `Given/When/Then` format describing the correct behavior.
65
-
3. Update TODO.md to note the new `@id` for the SE to implement.
66
-
4. SE implements the `@id` test in `tests/features/`**and** a `@given` Hypothesis property test in `tests/unit/`. Both are required.
63
+
1. Add a `@bug` Example to the relevant `Rule:` block in the `.feature` file using the standard `Given/When/Then` format describing the correct behavior.
64
+
2. Update TODO.md to note the new bug Example for the SE to implement.
65
+
3. SE implements the test in `tests/features/`**and** a `@given` Hypothesis property test in `tests/unit/`. Both are required.
Run `uv run task test-fast` once. It reads the in-progress `.feature` file, assigns `@id` tags to any untagged `Example:` blocks (writing them back to the `.feature` file), and generates `tests/features/<feature_slug>/<rule_slug>_test.py` — one file per `Rule:` block, one skipped function per `@id`. Verify the files were created, then stage all changes (including any `@id` write-backs to the `.feature` file).
147
+
148
+
Commit: `feat(<feature-stem>): add architecture and test stubs`
-[ ] Exactly one .feature `in_progress`. If not present, Load `skill feature-selection`
153
157
-[ ] Architecture stubs present in `<package>/` (committed by Step 2)
154
158
-[ ] Read `docs/architecture.md` — understand all architectural decisions before writing any test
155
-
-[ ] Test stub files exist in `tests/features/<feature-name>/<rule_slug>_test.py` — one file per `Rule:` block, all `@id` stub functions present with `@pytest.mark.skip`; if missing, write them now before entering RED
156
-
157
-
### Write Test Stubs (if not present)
158
-
159
-
For each `Rule:` block in the in-progress `.feature` file, create `tests/features/<feature-name>/<rule_slug>_test.py` if it does not already exist. Write one function per `@id` Example, all skipped:
160
-
161
-
```python
162
-
@pytest.mark.skip(reason="not yet implemented")
163
-
def test_<feature_slug>_<@id>() ->None:
164
-
"""
165
-
<@id steps raw text including new lines>
166
-
"""
167
-
```
159
+
-[ ] Test stub files exist in `tests/features/<feature_slug>/<rule_slug>_test.py` — generated by pytest-beehave at Step 2 end; if missing, re-run `uv run task test-fast` and commit the generated files before entering RED
168
160
169
161
### Build TODO.md Test List
170
162
171
163
1. List all `@id` tags from in-progress `.feature` file
172
164
2. Order: fewest dependencies first; most impactful within that set
173
165
3. Each `@id` = one TODO item, status: `pending`
174
-
4. Confirm each `@id` has a corresponding skipped stub in `tests/features/<feature-name>/` — if any are missing, add them before proceeding
166
+
4. Confirm each `@id` has a corresponding skipped stub in `tests/features/<feature_slug>/` — if any are missing, add them before proceeding
175
167
176
168
### Outer Loop — One @id at a time
177
169
@@ -182,7 +174,7 @@ For each pending `@id`:
182
174
```
183
175
INNER LOOP
184
176
├── RED
185
-
│ ├── Confirm stub for this @id exists in tests/features/<feature-name>/<rule_slug>.feature with @pytest.mark.skip
177
+
│ ├── Confirm stub for this @id exists in tests/features/<feature_slug>/<rule_slug>_test.py with @pytest.mark.skip
186
178
│ ├── Read existing stubs in `<package>/` — base the test on the current data model and signatures
187
179
│ ├── Write test body (Given/When/Then → Arrange/Act/Assert); remove @pytest.mark.skip
188
180
│ ├── Update <package> stub signatures as needed — edit the `.py` file directly
@@ -221,33 +213,35 @@ All must pass before Self-Declaration.
221
213
222
214
### Self-Declaration (once, after all quality gates pass)
223
215
216
+
<!-- This list has exactly 25 items — count before submitting. If your count ≠ 25, you missed one. -->
217
+
224
218
Communicate verbally to the reviewer. Answer honestly for each principle:
225
219
226
-
- YAGNI: no code without a failing test — AGREE/DISAGREE | file:line
227
-
- YAGNI: no speculative abstractions — AGREE/DISAGREE | file:line
228
-
- KISS: simplest solution that passes — AGREE/DISAGREE | file:line
229
-
- KISS: no premature optimization — AGREE/DISAGREE | file:line
230
-
- DRY: no duplication — AGREE/DISAGREE | file:line
231
-
- DRY: no redundant comments — AGREE/DISAGREE | file:line
232
-
- SOLID-S: one reason to change per class — AGREE/DISAGREE | file:line
233
-
- SOLID-O: open for extension, closed for modification — AGREE/DISAGREE | file:line
- OC-5: one dot per line — AGREE/DISAGREE | file:line
242
-
- OC-6: no abbreviations — AGREE/DISAGREE | file:line
243
-
- OC-7: ≤20 lines per function, ≤50 per class — AGREE/DISAGREE | longest: file:line
244
-
- OC-8: ≤2 instance variables per class (behavioural classes only; dataclasses, Pydantic models, value objects, and TypedDicts are exempt) — AGREE/DISAGREE | file:line
245
-
- OC-9: no getters/setters — AGREE/DISAGREE | file:line
246
-
- Patterns: I have no good reason to refactor parts of the code using OOP or Design Patterns — AGREE/DISAGREE | file:line
247
-
- Patterns: no creational smell — AGREE/DISAGREE | file:line
248
-
- Patterns: no structural smell — AGREE/DISAGREE | file:line
249
-
- Patterns: no behavioral smell — AGREE/DISAGREE | file:line
250
-
- Semantic: tests operate at same abstraction as AC — AGREE/DISAGREE | file:line
220
+
1. YAGNI: no code without a failing test — AGREE/DISAGREE | file:line
221
+
2. YAGNI: no speculative abstractions — AGREE/DISAGREE | file:line
222
+
3. KISS: simplest solution that passes — AGREE/DISAGREE | file:line
223
+
4. KISS: no premature optimization — AGREE/DISAGREE | file:line
224
+
5. DRY: no duplication — AGREE/DISAGREE | file:line
225
+
6. DRY: no redundant comments — AGREE/DISAGREE | file:line
226
+
7. SOLID-S: one reason to change per class — AGREE/DISAGREE | file:line
227
+
8. SOLID-O: open for extension, closed for modification — AGREE/DISAGREE | file:line
16. OC-5: one dot per line — AGREE/DISAGREE | file:line
236
+
17. OC-6: no abbreviations — AGREE/DISAGREE | file:line
237
+
18. OC-7: ≤20 lines per function, ≤50 per class — AGREE/DISAGREE | longest: file:line
238
+
19. OC-8: ≤2 instance variables per class (behavioural classes only; dataclasses, Pydantic models, value objects, and TypedDicts are exempt) — AGREE/DISAGREE | file:line
239
+
20. OC-9: no getters/setters — AGREE/DISAGREE | file:line
240
+
21. Patterns: no good reason remains to refactor using OOP or Design Patterns — AGREE/DISAGREE | file:line
241
+
22. Patterns: no creational smell — AGREE/DISAGREE | file:line
242
+
23. Patterns: no structural smell — AGREE/DISAGREE | file:line
243
+
24. Patterns: no behavioral smell — AGREE/DISAGREE | file:line
244
+
25. Semantic: tests operate at same abstraction as AC — AGREE/DISAGREE | file:line
251
245
252
246
A `DISAGREE` answer is not automatic rejection — state the reason and fix before handing off.
253
247
@@ -265,11 +259,11 @@ Signal completion to the reviewer. Provide:
265
259
### Test File Layout
266
260
267
261
```
268
-
tests/features/<feature-name>/<rule_slug>_test.py
262
+
tests/features/<feature_slug>/<rule_slug>_test.py
269
263
```
270
264
271
-
-`<feature-name>` = the `.feature` file stem
272
-
-`<rule_slug>` = the `Rule:` title slugified
265
+
-`<feature_slug>` = the `.feature` file stem with hyphens replaced by underscores, lowercase
266
+
-`<rule_slug>` = the `Rule:` title slugified (lowercase, underscores)
Copy file name to clipboardExpand all lines: .opencode/skills/scope/SKILL.md
+10-13Lines changed: 10 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -130,7 +130,7 @@ Append all answered Q&A to `docs/discovery_journal.md`, in groups (general, cros
130
130
Group headers use this format:
131
131
- General group: `### General`
132
132
- Cross-cutting group: `### <Group Name>`
133
-
- Feature group: `### Feature: <feature-name>`
133
+
- Feature group: `### Feature: <feature-stem>`
134
134
135
135
**Step B — Update .feature descriptions**
136
136
@@ -216,7 +216,7 @@ Avoid: "As the system, I want..." (no business value). Break down stories that c
216
216
-[ ] Rules collectively cover all entities in scope from the feature description
217
217
-[ ] Every Rule passes the INVEST gate
218
218
219
-
Commit: `feat(stories): write user stories for <name>`
219
+
Commit: `feat(stories): write user stories for <feature-stem>`
220
220
221
221
### Step B — Criteria
222
222
@@ -244,7 +244,6 @@ All Rules must have their pre-mortems completed before any Examples are written.
244
244
```
245
245
246
246
**Rules**:
247
-
-`@id` tag on the line before `Example:`
248
247
-`Example:` keyword (not `Scenario:`)
249
248
-`Given/When/Then` in plain English
250
249
-`Then` must be a single, observable, measurable outcome — no "and"
@@ -271,7 +270,6 @@ All Rules must have their pre-mortems completed before any Examples are written.
271
270
272
271
**Review checklist:**
273
272
-[ ] Every `Rule:` block has at least one Example
274
-
-[ ] Every `@id` is unique within this feature
275
273
-[ ] Every Example has `Given/When/Then`
276
274
-[ ] Every `Then` is a single, observable, measurable outcome
277
275
-[ ] No Example tests implementation details
@@ -291,15 +289,14 @@ Communicate verbally to the next agent. Every `DISAGREE` is a **hard blocker**
291
289
- No impl details: no Example tests internal state or implementation — AGREE/DISAGREE | file:line
292
290
- Coverage: every entity in the feature description appears in at least one Rule — AGREE/DISAGREE | missing:
293
291
- Distinct: no two Examples test the same observable behavior — AGREE/DISAGREE | file:line
294
-
- Unique IDs: all @id values are unique within this feature — AGREE/DISAGREE
295
292
- Pre-mortem: I ran a pre-mortem on each Rule and found no hidden failure modes — AGREE/DISAGREE | Rule:
296
293
- Scope: no Example introduces behavior outside the feature boundary — AGREE/DISAGREE | file:line
297
294
298
-
Commit: `feat(criteria): write acceptance criteria for <name>`
295
+
Commit: `feat(criteria): write acceptance criteria for <feature-stem>`
299
296
300
297
**After this commit, `Example:` blocks are frozen.** Any change requires:
301
298
1. Add `@deprecated` tag to the old Example
302
-
2. Write a new Example with a new `@id`
299
+
2. Write a new Example (the `@id` tag will be assigned automatically)
303
300
304
301
---
305
302
@@ -310,14 +307,14 @@ When a defect is reported against a completed or in-progress feature:
310
307
1.**PO** adds a new Example to the relevant `Rule:` block in the `.feature` file:
311
308
312
309
```gherkin
313
-
@bug @id:<new-8-char-hex>
310
+
@bug
314
311
Example: <what the bug is>
315
312
Given <conditions that trigger the bug>
316
313
When <action>
317
314
Then <correct behavior>
318
315
```
319
316
320
-
2.**SE** implements the specific test in `tests/features/<feature-name>/` (the `@id` test).
317
+
2.**SE** implements the specific test in `tests/features/<feature_slug>/` (the `@id` test).
321
318
3.**SE** also writes a `@given` Hypothesis property test in `tests/unit/` covering the whole class of inputs that triggered the bug — not just the single case.
322
319
4. Both tests are required — neither is optional.
323
320
5. SE follows the normal TDD loop (Step 3) for the new `@id`.
@@ -404,7 +401,7 @@ Status: IN-PROGRESS
404
401
|----|----------|--------|
405
402
| Q8 | ... | ... |
406
403
407
-
### Feature: <feature-name>
404
+
### Feature: <feature-stem>
408
405
409
406
| ID | Question | Answer |
410
407
|----|----------|--------|
@@ -435,7 +432,7 @@ success/failure conditions, and out-of-scope boundaries.>
435
432
(First session only. Omit this subsection in subsequent sessions.)
(Write "No changes" if no features were added or modified this session.)
440
437
441
438
### Domain Model
@@ -459,12 +456,12 @@ Rules:
459
456
460
457
---
461
458
462
-
## YYYY-MM-DD — <feature-name>: <shorttitle>
459
+
## YYYY-MM-DD — <feature-stem>: <shorttitle>
463
460
464
461
Decision: <what was decided — one sentence>
465
462
Reason: <why — one sentence>
466
463
Alternatives considered: <whatwasrejectedandwhy>
467
-
Feature: <feature-name>
464
+
Feature: <feature-stem>
468
465
```
469
466
470
467
Rules: Append-only. When a decision changes, append a new block that supersedes the old one. Cross-feature decisions use `Cross-feature:` in the header. Only write a block for non-obvious decisions with meaningful trade-offs.
Copy file name to clipboardExpand all lines: .opencode/skills/session-workflow/SKILL.md
+13-13Lines changed: 13 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -24,7 +24,7 @@ Every session starts by reading state. Every session ends by writing state. This
24
24
2.**If you are the PO** and Step 1 (SCOPE) is active: check `docs/discovery_journal.md` for the most recent session block.
25
25
- If the most recent block has `Status: IN-PROGRESS` → the previous session was interrupted. Resume it before starting a new session: finish updating `.feature` files and `docs/discovery.md`, then mark the block `Status: COMPLETE`.
@@ -79,15 +79,15 @@ Run @<agent-name> — <one concrete action>
79
79
80
80
**"Next" line format**: Always prefix with `Run @<agent-name>` so the human knows exactly which agent to invoke. Agent names are defined in `AGENTS.md` — use the name exactly as listed there. Examples:
0 commit comments