Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions .opencode/agents/product-owner.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,18 @@ When a gap is reported (by software-engineer or reviewer):

| Situation | Action |
|---|---|
| Edge case within current user stories | Add a new Example with a new `@id` to the relevant `.feature` file. |
| Edge case within current user stories | Add a new Example to the relevant `.feature` file. |
| New behavior beyond current stories | Add to backlog as a new feature. Do not extend the current feature. |
| Behavior contradicts an existing Example | Write a new Example with new `@id`. |
| Post-merge defect | Move the `.feature` file back to `in-progress/`, add new Example with `@id`, resume at Step 3. |
| Behavior contradicts an existing Example | Add `@deprecated` to the old Example; write a new Example. |
| Post-merge defect | Move the `.feature` file back to `in-progress/`, add new Example, resume at Step 3. |

## Bug Handling

When a defect is reported against any feature:

1. Add a `@bug @id:<new-8-char-hex>` Example to the relevant `Rule:` block in the `.feature` file.
2. Write the Example using the standard `Given/When/Then` format describing the correct behavior.
3. Update TODO.md to note the new `@id` for the SE to implement.
4. SE implements the `@id` test in `tests/features/` **and** a `@given` Hypothesis property test in `tests/unit/`. Both are required.
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.
2. Update TODO.md to note the new bug Example for the SE to implement.
3. SE implements the test in `tests/features/` **and** a `@given` Hypothesis property test in `tests/unit/`. Both are required.

## Available Skills

Expand Down
88 changes: 41 additions & 47 deletions .opencode/skills/implementation/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,12 @@ Place stubs where responsibility dictates — do not pre-create `ports/` or `ada
Append a new dated block to `docs/architecture.md` for each significant decision:

```markdown
## YYYY-MM-DD — <feature-name>: <short title>
## YYYY-MM-DD — <feature-stem>: <short title>

Decision: <what was decided>
Reason: <why, one sentence>
Alternatives considered: <what was rejected and why>
Feature: <feature-name>
Feature: <feature-stem>
```

Only write a block for non-obvious decisions with meaningful trade-offs. Routine YAGNI choices do not need a record.
Expand All @@ -141,7 +141,11 @@ Apply to the stub files just written:

If any check fails: fix the stub files before committing.

Commit: `feat(<feature-name>): add architecture stubs`
### Generate Test Stubs

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

Commit: `feat(<feature-stem>): add architecture and test stubs`

---

Expand All @@ -152,26 +156,14 @@ Commit: `feat(<feature-name>): add architecture stubs`
- [ ] Exactly one .feature `in_progress`. If not present, Load `skill feature-selection`
- [ ] Architecture stubs present in `<package>/` (committed by Step 2)
- [ ] Read `docs/architecture.md` — understand all architectural decisions before writing any test
- [ ] 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

### Write Test Stubs (if not present)

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:

```python
@pytest.mark.skip(reason="not yet implemented")
def test_<feature_slug>_<@id>() -> None:
"""
<@id steps raw text including new lines>
"""
```
- [ ] 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

### Build TODO.md Test List

1. List all `@id` tags from in-progress `.feature` file
2. Order: fewest dependencies first; most impactful within that set
3. Each `@id` = one TODO item, status: `pending`
4. Confirm each `@id` has a corresponding skipped stub in `tests/features/<feature-name>/` — if any are missing, add them before proceeding
4. Confirm each `@id` has a corresponding skipped stub in `tests/features/<feature_slug>/` — if any are missing, add them before proceeding

### Outer Loop — One @id at a time

Expand All @@ -182,7 +174,7 @@ For each pending `@id`:
```
INNER LOOP
├── RED
│ ├── Confirm stub for this @id exists in tests/features/<feature-name>/<rule_slug>.feature with @pytest.mark.skip
│ ├── Confirm stub for this @id exists in tests/features/<feature_slug>/<rule_slug>_test.py with @pytest.mark.skip
│ ├── Read existing stubs in `<package>/` — base the test on the current data model and signatures
│ ├── Write test body (Given/When/Then → Arrange/Act/Assert); remove @pytest.mark.skip
│ ├── Update <package> stub signatures as needed — edit the `.py` file directly
Expand Down Expand Up @@ -221,33 +213,35 @@ All must pass before Self-Declaration.

### Self-Declaration (once, after all quality gates pass)

<!-- This list has exactly 25 items — count before submitting. If your count ≠ 25, you missed one. -->

Communicate verbally to the reviewer. Answer honestly for each principle:

- YAGNI: no code without a failing test — AGREE/DISAGREE | file:line
- YAGNI: no speculative abstractions — AGREE/DISAGREE | file:line
- KISS: simplest solution that passes — AGREE/DISAGREE | file:line
- KISS: no premature optimization — AGREE/DISAGREE | file:line
- DRY: no duplication — AGREE/DISAGREE | file:line
- DRY: no redundant comments — AGREE/DISAGREE | file:line
- SOLID-S: one reason to change per class — AGREE/DISAGREE | file:line
- SOLID-O: open for extension, closed for modification — AGREE/DISAGREE | file:line
- SOLID-L: subtypes substitutable — AGREE/DISAGREE | file:line
- SOLID-I: no forced unused deps — AGREE/DISAGREE | file:line
- SOLID-D: depend on abstractions, not concretions — AGREE/DISAGREE | file:line
- OC-1: one level of indentation per method — AGREE/DISAGREE | deepest: file:line
- OC-2: no else after return — AGREE/DISAGREE | file:line
- OC-3: primitive types wrapped — AGREE/DISAGREE | file:line
- OC-4: first-class collections — AGREE/DISAGREE | file:line
- OC-5: one dot per line — AGREE/DISAGREE | file:line
- OC-6: no abbreviations — AGREE/DISAGREE | file:line
- OC-7: ≤20 lines per function, ≤50 per class — AGREE/DISAGREE | longest: file:line
- OC-8: ≤2 instance variables per class (behavioural classes only; dataclasses, Pydantic models, value objects, and TypedDicts are exempt) — AGREE/DISAGREE | file:line
- OC-9: no getters/setters — AGREE/DISAGREE | file:line
- Patterns: I have no good reason to refactor parts of the code using OOP or Design Patterns — AGREE/DISAGREE | file:line
- Patterns: no creational smell — AGREE/DISAGREE | file:line
- Patterns: no structural smell — AGREE/DISAGREE | file:line
- Patterns: no behavioral smell — AGREE/DISAGREE | file:line
- Semantic: tests operate at same abstraction as AC — AGREE/DISAGREE | file:line
1. YAGNI: no code without a failing test — AGREE/DISAGREE | file:line
2. YAGNI: no speculative abstractions — AGREE/DISAGREE | file:line
3. KISS: simplest solution that passes — AGREE/DISAGREE | file:line
4. KISS: no premature optimization — AGREE/DISAGREE | file:line
5. DRY: no duplication — AGREE/DISAGREE | file:line
6. DRY: no redundant comments — AGREE/DISAGREE | file:line
7. SOLID-S: one reason to change per class — AGREE/DISAGREE | file:line
8. SOLID-O: open for extension, closed for modification — AGREE/DISAGREE | file:line
9. SOLID-L: subtypes substitutable — AGREE/DISAGREE | file:line
10. SOLID-I: no forced unused deps — AGREE/DISAGREE | file:line
11. SOLID-D: depend on abstractions, not concretions — AGREE/DISAGREE | file:line
12. OC-1: one level of indentation per method — AGREE/DISAGREE | deepest: file:line
13. OC-2: no else after return — AGREE/DISAGREE | file:line
14. OC-3: primitive types wrapped — AGREE/DISAGREE | file:line
15. OC-4: first-class collections — AGREE/DISAGREE | file:line
16. OC-5: one dot per line — AGREE/DISAGREE | file:line
17. OC-6: no abbreviations — AGREE/DISAGREE | file:line
18. OC-7: ≤20 lines per function, ≤50 per class — AGREE/DISAGREE | longest: file:line
19. OC-8: ≤2 instance variables per class (behavioural classes only; dataclasses, Pydantic models, value objects, and TypedDicts are exempt) — AGREE/DISAGREE | file:line
20. OC-9: no getters/setters — AGREE/DISAGREE | file:line
21. Patterns: no good reason remains to refactor using OOP or Design Patterns — AGREE/DISAGREE | file:line
22. Patterns: no creational smell — AGREE/DISAGREE | file:line
23. Patterns: no structural smell — AGREE/DISAGREE | file:line
24. Patterns: no behavioral smell — AGREE/DISAGREE | file:line
25. Semantic: tests operate at same abstraction as AC — AGREE/DISAGREE | file:line

A `DISAGREE` answer is not automatic rejection — state the reason and fix before handing off.

Expand All @@ -265,11 +259,11 @@ Signal completion to the reviewer. Provide:
### Test File Layout

```
tests/features/<feature-name>/<rule_slug>_test.py
tests/features/<feature_slug>/<rule_slug>_test.py
```

- `<feature-name>` = the `.feature` file stem
- `<rule_slug>` = the `Rule:` title slugified
- `<feature_slug>` = the `.feature` file stem with hyphens replaced by underscores, lowercase
- `<rule_slug>` = the `Rule:` title slugified (lowercase, underscores)

### Function Naming

Expand Down Expand Up @@ -299,7 +293,7 @@ def test_<feature_slug>_<@id>() -> None:
### Markers

- `@pytest.mark.slow` — takes > 50ms (Hypothesis, DB, network, terminal I/O)
- `@pytest.mark.deprecated` — auto-skipped by conftest; used for superseded Examples
- `@pytest.mark.deprecated` — auto-skipped by pytest-beehave; used for superseded Examples

```python
@pytest.mark.deprecated
Expand Down
2 changes: 1 addition & 1 deletion .opencode/skills/living-docs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ If `docs/glossary.md` already exists:
**When run standalone** (stakeholder on demand): commit after all diagrams and glossary are updated:

```
docs(living-docs): update C4 and glossary after <feature-name>
docs(living-docs): update C4 and glossary after <feature-stem>
```

If triggered without a specific feature (general refresh):
Expand Down
4 changes: 2 additions & 2 deletions .opencode/skills/pr-management/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Create and manage pull requests after the reviewer approves the feature (Step 5)
## Branch Naming

```
feature/<feature-name> # new feature
feature/<feature-stem> # new feature
fix/<issue-description> # bug fix
refactor/<scope> # refactoring
docs/<scope> # documentation
Expand Down Expand Up @@ -42,7 +42,7 @@ git commit -m "chore(deps): add python-dotenv dependency"

```bash
# Push branch
git push -u origin feature/<feature-name>
git push -u origin feature/<feature-stem>

# Create PR
gh pr create \
Expand Down
6 changes: 3 additions & 3 deletions .opencode/skills/refactor/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,9 @@ Refactoring commits are always **separate** from feature commits.

| Commit type | Message format | When |
|---|---|---|
| Preparatory refactoring | `refactor(<feature-name>): <what>` | Before RED, to make the feature easier |
| REFACTOR phase | `refactor(<feature-name>): <what>` | After GREEN, cleaning up the green code |
| Feature addition | `feat(<feature-name>): <what>` | After GREEN (never mixed with refactor) |
| Preparatory refactoring | `refactor(<feature-stem>): <what>` | Before RED, to make the feature easier |
| REFACTOR phase | `refactor(<feature-stem>): <what>` | After GREEN, cleaning up the green code |
| Feature addition | `feat(<feature-stem>): <what>` | After GREEN (never mixed with refactor) |

Never mix a structural cleanup with a behavior addition in one commit. This keeps history bisectable and CI green at every commit.

Expand Down
23 changes: 10 additions & 13 deletions .opencode/skills/scope/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ Append all answered Q&A to `docs/discovery_journal.md`, in groups (general, cros
Group headers use this format:
- General group: `### General`
- Cross-cutting group: `### <Group Name>`
- Feature group: `### Feature: <feature-name>`
- Feature group: `### Feature: <feature-stem>`

**Step B — Update .feature descriptions**

Expand Down Expand Up @@ -216,7 +216,7 @@ Avoid: "As the system, I want..." (no business value). Break down stories that c
- [ ] Rules collectively cover all entities in scope from the feature description
- [ ] Every Rule passes the INVEST gate

Commit: `feat(stories): write user stories for <name>`
Commit: `feat(stories): write user stories for <feature-stem>`

### Step B — Criteria

Expand Down Expand Up @@ -244,7 +244,6 @@ All Rules must have their pre-mortems completed before any Examples are written.
```

**Rules**:
- `@id` tag on the line before `Example:`
- `Example:` keyword (not `Scenario:`)
- `Given/When/Then` in plain English
- `Then` must be a single, observable, measurable outcome — no "and"
Expand All @@ -271,7 +270,6 @@ All Rules must have their pre-mortems completed before any Examples are written.

**Review checklist:**
- [ ] Every `Rule:` block has at least one Example
- [ ] Every `@id` is unique within this feature
- [ ] Every Example has `Given/When/Then`
- [ ] Every `Then` is a single, observable, measurable outcome
- [ ] No Example tests implementation details
Expand All @@ -291,15 +289,14 @@ Communicate verbally to the next agent. Every `DISAGREE` is a **hard blocker**
- No impl details: no Example tests internal state or implementation — AGREE/DISAGREE | file:line
- Coverage: every entity in the feature description appears in at least one Rule — AGREE/DISAGREE | missing:
- Distinct: no two Examples test the same observable behavior — AGREE/DISAGREE | file:line
- Unique IDs: all @id values are unique within this feature — AGREE/DISAGREE
- Pre-mortem: I ran a pre-mortem on each Rule and found no hidden failure modes — AGREE/DISAGREE | Rule:
- Scope: no Example introduces behavior outside the feature boundary — AGREE/DISAGREE | file:line

Commit: `feat(criteria): write acceptance criteria for <name>`
Commit: `feat(criteria): write acceptance criteria for <feature-stem>`

**After this commit, `Example:` blocks are frozen.** Any change requires:
1. Add `@deprecated` tag to the old Example
2. Write a new Example with a new `@id`
2. Write a new Example (the `@id` tag will be assigned automatically)

---

Expand All @@ -310,14 +307,14 @@ When a defect is reported against a completed or in-progress feature:
1. **PO** adds a new Example to the relevant `Rule:` block in the `.feature` file:

```gherkin
@bug @id:<new-8-char-hex>
@bug
Example: <what the bug is>
Given <conditions that trigger the bug>
When <action>
Then <correct behavior>
```

2. **SE** implements the specific test in `tests/features/<feature-name>/` (the `@id` test).
2. **SE** implements the specific test in `tests/features/<feature_slug>/` (the `@id` test).
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.
4. Both tests are required — neither is optional.
5. SE follows the normal TDD loop (Step 3) for the new `@id`.
Expand Down Expand Up @@ -404,7 +401,7 @@ Status: IN-PROGRESS
|----|----------|--------|
| Q8 | ... | ... |

### Feature: <feature-name>
### Feature: <feature-stem>

| ID | Question | Answer |
|----|----------|--------|
Expand Down Expand Up @@ -435,7 +432,7 @@ success/failure conditions, and out-of-scope boundaries.>
(First session only. Omit this subsection in subsequent sessions.)

### Feature List
- `<feature-name>` — <one-sentence description of what changed or was added>
- `<feature-stem>` — <one-sentence description of what changed or was added>
(Write "No changes" if no features were added or modified this session.)

### Domain Model
Expand All @@ -459,12 +456,12 @@ Rules:

---

## YYYY-MM-DD — <feature-name>: <short title>
## YYYY-MM-DD — <feature-stem>: <short title>

Decision: <what was decided — one sentence>
Reason: <why — one sentence>
Alternatives considered: <what was rejected and why>
Feature: <feature-name>
Feature: <feature-stem>
```

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.
Expand Down
26 changes: 13 additions & 13 deletions .opencode/skills/session-workflow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Every session starts by reading state. Every session ends by writing state. This
2. **If you are the PO** and Step 1 (SCOPE) is active: check `docs/discovery_journal.md` for the most recent session block.
- 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`.
3. If a feature is active at Step 2–5, read:
- `docs/features/in-progress/<name>.feature` — feature file (Rules + Examples + @id)
- `docs/features/in-progress/<feature-stem>.feature` — feature file (Rules + Examples + @id)
- `docs/discovery.md` — project-level synthesis changelog (for context)
4. Run `git status` — understand what is committed vs. what is not
5. Confirm scope: you are working on exactly one step of one feature
Expand All @@ -43,7 +43,7 @@ Every session starts by reading state. Every session ends by writing state. This
2. Commit any uncommitted work (even WIP):
```bash
git add -A
git commit -m "WIP(<feature-name>): <what was done>"
git commit -m "WIP(<feature-stem>): <what was done>"
```
3. If a step is fully complete, use the proper commit message instead of WIP.

Expand All @@ -55,7 +55,7 @@ When a step completes within a session:
2. Commit the TODO.md update:
```bash
git add TODO.md
git commit -m "chore: complete step <N> for <feature-name>"
git commit -m "chore: complete step <N> for <feature-stem>"
```
3. Only then begin the next step (in a new session where possible — see Rule 4).

Expand All @@ -64,9 +64,9 @@ When a step completes within a session:
```markdown
# Current Work

Feature: <name>
Feature: <feature-stem>
Step: <1-5> (<step name>)
Source: docs/features/in-progress/<name>.feature
Source: docs/features/in-progress/<feature-stem>.feature

## Progress
- [x] `@id:<hex>`: <description>
Expand All @@ -79,15 +79,15 @@ Run @<agent-name> — <one concrete action>

**"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:
- `Run @<software-engineer-agent> — implement @id:a1b2c3d4 (Step 3 RED)`
- `Run @<software-engineer-agent> — load skill implementation and begin Step 2 (Architecture) for <feature-name>`
- `Run @<reviewer-agent> — verify feature <feature-name> at Step 4`
- `Run @<software-engineer-agent> — load skill implementation and begin Step 2 (Architecture) for <feature-stem>`
- `Run @<reviewer-agent> — verify feature <feature-stem> at Step 4`
- `Run @<product-owner-agent> — pick next BASELINED feature from backlog`
- `Run @<product-owner-agent> — accept feature <feature-name> at Step 5`
- `Run @<product-owner-agent> — accept feature <feature-stem> at Step 5`

**Source path by step:**
- Step 1: `Source: docs/features/backlog/<name>.feature`
- Steps 2–4: `Source: docs/features/in-progress/<name>.feature`
- Step 5: `Source: docs/features/completed/<name>.feature`
- Step 1: `Source: docs/features/backlog/<feature-stem>.feature`
- Steps 2–4: `Source: docs/features/in-progress/<feature-stem>.feature`
- Step 5: `Source: docs/features/completed/<feature-stem>.feature`

Status markers:
- `[ ]` — not started
Expand All @@ -110,9 +110,9 @@ During Step 3 (TDD Loop), TODO.md **must** include a `## Cycle State` block to t
```markdown
# Current Work

Feature: <name>
Feature: <feature-stem>
Step: 3 (TDD Loop)
Source: docs/features/in-progress/<name>.feature
Source: docs/features/in-progress/<feature-stem>.feature

## Cycle State
Test: `@id:<hex>` — <description>
Expand Down
Loading
Loading