mit einem Klick
implement
Step 3 — TDD Loop, one @id at a time
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Menü
Step 3 — TDD Loop, one @id at a time
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Basierend auf der SOC-Berufsklassifikation
Step 2 — Architecture and domain design, one feature at a time
Step 1 — discover requirements through stakeholder interviews and write Gherkin acceptance criteria
Generate and update architecture diagrams, living glossary, and system overview from existing project docs
Enforce code quality using ruff, pytest coverage, and static type checking
Create pull requests with conventional commits, proper formatting, and branch workflow
Flow protocol — design and operate state machine workflows with FLOW.md + WORK.md
| name | implement |
| description | Step 3 — TDD Loop, one @id at a time |
| version | 5.0 |
| author | software-engineer |
| audience | software-engineer |
| workflow | feature-lifecycle |
Step 3: RED → GREEN → REFACTOR, one @id at a time. The software-engineer owns this step entirely.
Load this skill when continuing Step 3 (TDD Loop) for an in-progress feature. Architecture stubs must already exist (created by the system-architect at Step 2).
During implementation, correctness priorities are (in order):
test-fast still passeslint, static-check, full test with coverage run at end-of-feature handoffDesign correctness is far more important than lint/pyright/coverage compliance. Never run lint (ruff check, ruff format), static-check (pyright), or coverage during the TDD loop — those are handoff-only checks.
in_progress. If not present, load skill select-featurefeat/<stem> or fix/<stem> branch (git branch --show-current). If on main, load skill version-control and create/switch to the branch first<package>/ (committed by Step 2)docs/system.md — understand current system structure and constraints.feature file — understand acceptance criteriatests/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@id tags from in-progress .feature file@id = one TODO item, status: pending@id has a corresponding skipped stub in tests/features/<feature_slug>/ — if any are missing, add them before proceedingWIP limit: exactly one in_progress at all times.
For each pending @id:
INNER LOOP
├── RED
│ ├── 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
│ ├── uv run task test-fast
│ └── EXIT: this @id FAILS
│ (if it passes: test is wrong — fix it first)
│
├── GREEN
│ ├── Write minimum code — YAGNI + KISS only
│ │ (no DRY, SOLID, OC, Docstring, type hint here — those belong in REFACTOR)
│ ├── uv run task test-fast
│ └── EXIT: this @id passes AND all prior tests pass
│ (fix implementation only; do not advance to next @id)
│
└── REFACTOR
├── Load `skill refactor` — follow its Step-by-Step for this phase
├── uv run task test-fast after each individual change
└── EXIT: test-fast passes; no smells remain
Commit when a meaningful increment is green
Commit when a meaningful increment is green
uv run task lint
uv run task static-check
uv run task test-coverage # coverage must pass
timeout 10s uv run task run
If coverage is below the threshold: add test in tests/unit/ for uncovered branch (do NOT add @id tests for coverage).
All must pass before Self-Declaration.
Communicate verbally to the system-architect. Answer honestly for each principle:
As a software-engineer I declare that:
A DISAGREE answer is not automatic rejection — state the reason and fix before handing off.
Before signalling completion:
git status — working tree must be clean. Commit any remaining changes.git branch --show-current — must be feat/<stem> or fix/<stem>, never main.git log main..HEAD --oneline — must show 1+ commits. If empty, nothing was committed on this branch.git push origin $(git branch --show-current) — all commits must be on origin.Signal completion to the system-architect. Provide:
tests/features/<feature_slug>/<rule_slug>_test.py
<feature_slug> = the .feature file stem with hyphens replaced by underscores, lowercase<rule_slug> = the Rule: title slugified (lowercase, underscores)def test_<feature_slug>_<@id>() -> None:
feature_slug = the .feature file stem with spaces/hyphens replaced by underscores, lowercase@id = the @id from the Example: blockNew tests start as skipped stubs. Remove @pytest.mark.skip when implementing in the RED phase.
@pytest.mark.skip(reason="not yet implemented")
def test_<feature_slug>_<@id>() -> None:
"""
<@id steps raw text including new lines>
"""
Rules:
Gherkin steps as raw text on separate indented lines@id suffix@pytest.mark.slow — takes > 50ms (Hypothesis, DB, network, terminal I/O)@pytest.mark.deprecated — auto-skipped by pytest-beehave; used for replaced Examples@pytest.mark.deprecated
def test_wall_bounce_a3f2b1c4() -> None:
...
@pytest.mark.slow
def test_checkout_flow_b2c3d4e5() -> None:
...
When using @given in tests/unit/:
@pytest.mark.slow
@given(x=st.floats(min_value=-100, max_value=100, allow_nan=False))
@example(x=0.0)
def test_wall_bounce_c4d5e6f7(x: float) -> None:
"""
Given: Any floating point input value
When: compute_distance is called
Then: The result is >= 0
"""
assume(x != 0.0)
result = compute_distance(x)
assert result >= 0
Rules:
@pytest.mark.slow is mandatory on every @given-decorated test@example(...) is optional but encouragedThe test's Given/When/Then must operate at the same abstraction level as the AC's Steps.
| AC says | Test must do |
|---|---|
| "When the user presses W" | Send "W" through the actual input mechanism |
"When update_player receives 'W'" | Call update_player("W") directly |
If testing through the real entry point is infeasible, escalate to PO to adjust the AC boundary.
isinstance(), type(), or internal attribute (_x) checks in assertionsassert ok if they verify the same thing)pytest.mark.xfail without written justificationpytest.mark.skip(reason="not yet implemented") is only valid on stubs — remove it when implementing| Situation | Location | Tool |
|---|---|---|
Deterministic scenario from a .feature @id | tests/features/ | Plain pytest |
| Property holding across many input values | tests/unit/ | Hypothesis @given |
| Specific behavior or single edge case | tests/unit/ | Plain pytest |
| Stateful system with sequences of operations | tests/unit/ | Hypothesis stateful testing |
If during implementation you discover a behavior not covered by existing acceptance criteria:
.feature fileExtra tests in tests/unit/ are allowed freely (coverage, edge cases, etc.) — these do not need @id traceability.
signatures are written during Step 2 (Architecture) by the system-architect and refined during Step 3 (RED) by the software-engineer. They live directly in the package .py files — never in the .feature file.
Key rules:
... in the architecture stub... with the minimum implementationUse Python Protocols for external dependencies if they are identified in scope — never depend on a concrete class directly:
from typing import Protocol
from dataclasses import dataclass
@dataclass(frozen=True, slots=True)
class EmailAddress:
value: str
def validate(self) -> None: ...
class UserRepository(Protocol):
def save(self, user: "User") -> None: ...
def find_by_email(self, email: EmailAddress) -> "User | None": ...
Base directory for this skill: .opencode/skills/implement/
Relative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.
Note: file list is sampled.