CLAUDE.md@src/claude_code_hooks_daemon/strategies/tdd · git:20260807.dc1513d · 2026-08-07 · sha256 2afd03001385ef6b
CLAUDE.md@src/claude_code_hooks_daemon/strategies/tdd git:20260807.dc1513dA
Immutable. This exact content is served forever at /api/v1/blob/2afd03001385ef6b.
# TDD Strategy Pattern - Archetype Documentation
**Status**: Reference Architecture
**Pattern**: Strategy Pattern (GoF) with Protocol interface
**Purpose**: Language-specific TDD enforcement with zero coupling to the handler
This module is the **canonical archetype** for all language-aware strategy implementations in the hooks daemon. Any future handler that needs language-specific behaviour MUST follow this pattern exactly.
---
## Architecture Overview
```
TddEnforcementHandler (Orchestrator)
│
│ Zero language logic. Pure workflow.
│
├── TddStrategyRegistry (Lookup)
│ │
│ │ Maps file extensions → strategy instances
│ │
│ └── TddStrategy (Protocol Interface)
│ │
│ │ Contract: 7 members (2 properties + 5 methods),
│ │ structural typing
│ │
│ ├── PythonTddStrategy
│ ├── GoTddStrategy
│ ├── JavaScriptTddStrategy (covers JS + TS)
│ ├── PhpTddStrategy
│ ├── RustTddStrategy
│ ├── JavaTddStrategy
│ ├── CSharpTddStrategy
│ ├── KotlinTddStrategy
│ ├── RubyTddStrategy
│ ├── SwiftTddStrategy
│ └── DartTddStrategy
│
└── common.py (DRY Utilities)
│
├── COMMON_TEST_DIRECTORIES (shared constant)
├── is_in_common_test_directory()
└── matches_directory()
```
### Separation of Concerns (SOLID)
| Component | Responsibility | Knows About Languages? |
| ------------------ | ---------------------------------------------------- | ---------------------- |
| **Handler** | Orchestrates workflow (matches → handle → test path) | NO |
| **Protocol** | Defines the contract all strategies must satisfy | NO |
| **Registry** | Maps extensions to strategy instances | NO (data-driven) |
| **Strategy** | ALL language-specific logic for one language | YES (its own only) |
| **Common** | Shared utilities used by multiple strategies | NO |
| **LanguageConfig** | Pure config data (names, extensions) | N/A (data only) |
---
## The Protocol (Interface Contract)
```python
@runtime_checkable
class TddStrategy(Protocol):
@property
def language_name(self) -> str: ...
@property
def extensions(self) -> tuple[str, ...]: ...
def is_test_file(self, file_path: str) -> bool: ...
def is_production_source(self, file_path: str) -> bool: ...
def should_skip(self, file_path: str, content: str = "") -> bool: ...
def compute_test_filename(self, source_filename: str) -> str: ...
def get_acceptance_tests(self) -> list[Any]: ...
```
### Why Protocol, Not ABC?
| Feature | Protocol (chosen) | ABC |
| ------------- | ------------------------------------- | --------------------- |
| Typing | Structural (duck typing) | Nominal (inheritance) |
| Coupling | Zero - no base class import needed | Tight - must inherit |
| Testing | Any object satisfying shape works | Must subclass ABC |
| Runtime check | `@runtime_checkable` + `isinstance()` | `isinstance()` |
| Python idiom | Pythonic, modern (PEP 544) | Traditional OOP |
**Decision**: Protocol enables true Open/Closed Principle. New strategies need zero imports from the framework - they just need to match the shape.
### Method Contract Details
#### `language_name: str` (property)
- Human-readable name for error messages (e.g., `"Python"`, `"JavaScript/TypeScript"`)
- Used in TDD denial messages shown to the developer
- Must be stable - changing it changes user-facing output
#### `extensions: tuple[str, ...]` (property)
- File extensions this strategy handles (e.g., `(".py",)`, `(".js", ".jsx", ".ts", ".tsx")`)
- Must include the dot prefix
- Must be lowercase
- Used by Registry for extension-to-strategy mapping
- One strategy can handle multiple extensions (JS/TS pattern)
#### `is_test_file(file_path: str) -> bool`
- Returns `True` if the file IS a test file (should be allowed through)
- MUST check common test directories via `is_in_common_test_directory()`
- MUST check language-specific naming patterns (e.g., `test_*.py`, `*_test.go`)
- Called by handler's `matches()` to skip test files
#### `is_production_source(file_path: str) -> bool`
- Returns `True` if the file is in a production source directory
- Checks language-specific source directory conventions (e.g., `/src/`, `/lib/`, `/app/`)
- Should exclude language-specific init/config files (e.g., Python's `__init__.py`)
- Called by handler's `matches()` to identify files that need TDD enforcement
#### `should_skip(file_path: str, content: str = "") -> bool`
- Returns `True` if the file should be skipped entirely (no TDD enforcement)
- Checks for vendor dirs, build output, virtual environments, etc.
- The optional `content` argument enables content-based skipping where the path
alone is not decisive (e.g. PHP interface declarations). Strategies that do
not need it still MUST accept it — the parameter is part of the contract, so
the handler can call every strategy identically
- Called by handler's `matches()` before any other checks
#### `compute_test_filename(source_filename: str) -> str`
- Takes a source filename (not full path), returns expected test filename
- Language-specific transformation:
- Python: `module.py` -> `test_module.py`
- Go: `server.go` -> `server_test.go`
- JS/TS: `helpers.ts` -> `helpers.test.ts`
- PHP: `UserService.php` -> `UserServiceTest.php`
- Java: `UserService.java` -> `UserServiceTest.java`
- Rust: `parser.rs` -> `parser_test.rs`
- C#: `UserService.cs` -> `UserServiceTests.cs`
- Kotlin: `UserService.kt` -> `UserServiceTest.kt`
- Ruby: `user_service.rb` -> `user_service_spec.rb`
- Swift: `Parser.swift` -> `ParserTests.swift`
- Dart: `parser.dart` -> `parser_test.dart`
- Used by handler to construct the expected test file path
#### `get_acceptance_tests(self) -> list[Any]` (MANDATORY)
- Returns a list of `AcceptanceTest` instances for this language
- MUST return at least one test (enforced by QA checker)
- Tests should be BLOCKING type (expected_decision=Decision.DENY)
- Tests use `/tmp/acceptance-test-tdd-{language}/` paths for safety
- Must include `setup_commands` (create temp dirs) and `cleanup_commands` (remove them)
- `expected_message_patterns` MUST include the exact `language_name` string
- Handler aggregates tests from all strategies via `get_acceptance_tests()`
- See existing strategies for examples
---
## Acceptance Test Provision (MANDATORY)
Every strategy MUST provide acceptance tests. This is enforced by:
1. **Protocol contract**: `get_acceptance_tests()` is part of the TddStrategy Protocol
2. **Unit tests**: `test_acceptance_tests.py` validates all 11 strategies provide valid tests
3. **QA checker**: `strategy_pattern_checker.py` AST-checks for missing `get_acceptance_tests()`
### How It Works
```
Strategy (provides tests) Handler (aggregates) Playbook (executes)
┌─────────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ PythonTddStrategy │─────▶│ │ │ │
│ GoTddStrategy │─────▶│ TddEnforcement │─────▶│ Acceptance Test │
│ JavaScriptStrategy │─────▶│ Handler │ │ Runner │
│ ... (11 total) │─────▶│ .get_acceptance │ │ (Haiku agents) │
│ │ │ _tests() │ │ │
└─────────────────────┘ └──────────────────┘ └─────────────────┘
Each returns 1+ test Collects & dedupes Runs in parallel
```
### AcceptanceTest Structure
```python
from claude_code_hooks_daemon.core import AcceptanceTest, Decision, TestType
AcceptanceTest(
title="TDD Enforcement - {Language} source without test",
command='Write file_path="/tmp/acceptance-test-tdd-{lang}/src/pkg/example.{ext}" content="..."',
description="Should block creating {Language} source file without test",
expected_decision=Decision.DENY,
expected_message_patterns=["TDD REQUIRED", "{Language}", "test file"],
test_type=TestType.BLOCKING,
safety_notes="Uses /tmp path, cleaned up after test",
setup_commands=["mkdir -p /tmp/acceptance-test-tdd-{lang}/src/pkg"],
cleanup_commands=["rm -rf /tmp/acceptance-test-tdd-{lang}"],
)
```
### Rules
1. **Every strategy provides its own tests** - strategies own their test definitions
2. **Handler is a thin aggregator** - collects from all strategies, adds nothing of its own
3. **No duplicate languages** - handler deduplicates by `language_name`
4. **Tests use safe paths** - `/tmp/` only, with cleanup commands
5. **Patterns include language name** - exact match for deduplication verification
---
## Strategy Implementation Pattern
Every strategy follows this exact structure. No exceptions.
### Template
```python
"""[Language] TDD strategy implementation."""
from pathlib import Path # Only if needed for stem/name operations
from claude_code_hooks_daemon.strategies.tdd.common import (
is_in_common_test_directory,
matches_directory,
)
# ── Language-specific constants (NO MAGIC STRINGS) ──────────────
_LANGUAGE_NAME = "[Language]"
_EXTENSIONS: tuple[str, ...] = (".[ext]",)
_SOURCE_DIRECTORIES: tuple[str, ...] = ("/src/",)
_SKIP_DIRECTORIES: tuple[str, ...] = ("vendor/",)
# Any additional constants specific to this language
# e.g., _INIT_FILENAME, _TEST_SUFFIX, _TEST_PREFIX
class [Language]TddStrategy:
"""TDD enforcement strategy for [Language] projects.
Test convention: source.[ext] -> [test pattern]
Source directories: [list]
"""
@property
def language_name(self) -> str:
return _LANGUAGE_NAME
@property
def extensions(self) -> tuple[str, ...]:
return _EXTENSIONS
def is_test_file(self, file_path: str) -> bool:
# ALWAYS check common directories first (DRY)
if is_in_common_test_directory(file_path):
return True
# Then check language-specific naming pattern
...
def is_production_source(self, file_path: str) -> bool:
# Exclude init/config files if applicable
...
# Check source directories
return matches_directory(file_path, _SOURCE_DIRECTORIES)
def should_skip(self, file_path: str, content: str = "") -> bool:
# Accept `content` even when unused - it is part of the Protocol contract
# so the handler can call every strategy with the same arguments.
return matches_directory(file_path, _SKIP_DIRECTORIES)
def compute_test_filename(self, source_filename: str) -> str:
# Language-specific transformation
...
def get_acceptance_tests(self) -> list[Any]:
from claude_code_hooks_daemon.core import AcceptanceTest, Decision, TestType
return [
AcceptanceTest(
title="TDD Enforcement - [Language] source without test",
command='Write file_path="/tmp/acceptance-test-tdd-[lang]/src/pkg/example.[ext]" content="..."',
description="Should block creating [Language] source without test",
expected_decision=Decision.DENY,
expected_message_patterns=["TDD REQUIRED", _LANGUAGE_NAME, "test file"],
test_type=TestType.BLOCKING,
safety_notes="Uses /tmp path, cleaned up after test",
setup_commands=["mkdir -p /tmp/acceptance-test-tdd-[lang]/src/pkg"],
cleanup_commands=["rm -rf /tmp/acceptance-test-tdd-[lang]"],
)
]
```
### Rules
1. **ALL strings are named constants** - Module-level, prefixed with `_`
2. **Always use `common.is_in_common_test_directory()`** in `is_test_file()` - DRY
3. **Always use `common.matches_directory()`** for directory matching - DRY
4. **No imports from other strategies** - Each strategy is fully independent
5. **No imports from the handler** - Strategies know nothing about handlers
6. **No imports from LanguageConfig** - Config is config, strategy is behaviour (SRP)
7. **Class does NOT inherit from anything** - Protocol is structural, not nominal
8. **One file per language** - Named `{language}_strategy.py`
---
## Registry
The `TddStrategyRegistry` is a simple extension-to-strategy map:
```python
registry = TddStrategyRegistry()
registry.register(PythonTddStrategy()) # Registers ".py"
registry.register(GoTddStrategy()) # Registers ".go"
strategy = registry.get_strategy("/workspace/src/app/server.go")
# Returns GoTddStrategy instance (matched ".go")
strategy = registry.get_strategy("/workspace/README.md")
# Returns None (no strategy for ".md")
```
### `create_default()` Factory
The `create_default()` class method creates a registry with ALL built-in strategies. This is the standard entry point used by the handler.
**Adding a new strategy**: Add one line to `create_default()`:
```python
registry.register(NewLanguageTddStrategy())
```
---
## Shared Utilities (DRY)
`common.py` contains logic used by multiple strategies:
### `COMMON_TEST_DIRECTORIES`
```python
COMMON_TEST_DIRECTORIES: tuple[str, ...] = (
"/tests/",
"/test/",
"/__tests__/",
"/spec/",
)
```
Cross-language test directory names. Every strategy's `is_test_file()` checks these first.
### `is_in_common_test_directory(file_path: str) -> bool`
Quick check if a file is in any common test directory. Called by ALL strategies in `is_test_file()`.
### `matches_directory(file_path: str, directories: tuple[str, ...]) -> bool`
Generic directory pattern matcher. Handles normalization (leading `/`, trailing `/`). Used by strategies for both `is_production_source()` and `should_skip()`.
### When to Add to Common vs Strategy
| Logic | Where | Why |
| ------------------------------------------ | ----------- | ----------------- |
| Test directory names used by 3+ languages | `common.py` | DRY |
| Directory matching utility | `common.py` | DRY |
| Test filename pattern (e.g., `test_*.py`) | Strategy | Language-specific |
| Source directory list (e.g., `/src/main/`) | Strategy | Language-specific |
| Skip directory list (e.g., `vendor/`) | Strategy | Language-specific |
| Init file exclusion (e.g., `__init__.py`) | Strategy | Language-specific |
**Rule**: If 3+ strategies share the same logic, extract to `common.py`. Otherwise keep it in the strategy.
---
## How to Add a New Language
### Step-by-Step (TDD)
#### 1. Create the test file FIRST
```bash
# File: tests/unit/strategies/tdd/test_{language}_strategy.py
```
Write comprehensive tests covering:
```python
"""Tests for {Language} TDD strategy."""
import pytest
from claude_code_hooks_daemon.strategies.tdd.{language}_strategy import {Language}TddStrategy
@pytest.fixture
def strategy() -> {Language}TddStrategy:
return {Language}TddStrategy()
class TestProperties:
def test_language_name(self, strategy: {Language}TddStrategy) -> None:
assert strategy.language_name == "{Language}"
def test_extensions(self, strategy: {Language}TddStrategy) -> None:
assert strategy.extensions == (".[ext]",)
class TestIsTestFile:
def test_common_test_directory(self, strategy: {Language}TddStrategy) -> None:
assert strategy.is_test_file("/workspace/tests/foo.[ext]") is True
def test_language_specific_pattern(self, strategy: {Language}TddStrategy) -> None:
assert strategy.is_test_file("/workspace/src/test_foo.[ext]") is True
def test_production_file(self, strategy: {Language}TddStrategy) -> None:
assert strategy.is_test_file("/workspace/src/foo.[ext]") is False
class TestIsProductionSource:
def test_in_source_directory(self, strategy: {Language}TddStrategy) -> None:
assert strategy.is_production_source("/workspace/src/foo.[ext]") is True
def test_outside_source(self, strategy: {Language}TddStrategy) -> None:
assert strategy.is_production_source("/workspace/foo.[ext]") is False
class TestShouldSkip:
def test_vendor_directory(self, strategy: {Language}TddStrategy) -> None:
assert strategy.should_skip("/workspace/vendor/foo.[ext]") is True
def test_normal_path(self, strategy: {Language}TddStrategy) -> None:
assert strategy.should_skip("/workspace/src/foo.[ext]") is False
class TestComputeTestFilename:
def test_standard_file(self, strategy: {Language}TddStrategy) -> None:
assert strategy.compute_test_filename("foo.[ext]") == "[expected_test_name]"
```
#### 2. Run tests - they MUST FAIL
```bash
pytest tests/unit/strategies/tdd/test_{language}_strategy.py -v
# Expected: ImportError (no implementation yet)
```
#### 3. Create the strategy implementation
```bash
# File: src/claude_code_hooks_daemon/strategies/tdd/{language}_strategy.py
```
Follow the template above exactly.
#### 4. Run tests - they MUST PASS
```bash
pytest tests/unit/strategies/tdd/test_{language}_strategy.py -v
# Expected: ALL PASS
```
#### 5. Register in the Registry
Edit `registry.py`:
```python
# In create_default():
from claude_code_hooks_daemon.strategies.tdd.{language}_strategy import {Language}TddStrategy
registry.register({Language}TddStrategy())
```
#### 6. Update registry tests
Edit `tests/unit/strategies/tdd/test_tdd_strategy_registry.py`:
- Add language to `test_create_default_registers_all_languages` expected list
- Add extension test case to `test_get_strategy_returns_correct_strategy`
#### 7. Run full QA
```bash
./scripts/qa/run_all.sh
# Expected: ALL CHECKS PASSED
```
#### 8. Restart daemon
```bash
.claude/hooks-daemon/bin/hooks-daemon restart
.claude/hooks-daemon/bin/hooks-daemon status
# Expected: RUNNING
```
---
## Registered Languages (11 total)
| Language | Strategy Class | Extensions | Test Pattern | Source Dirs |
| --------------------- | ----------------------- | ---------------------------- | ------------------------ | --------------------------------------- |
| Python | `PythonTddStrategy` | `.py` | `test_*.py` | `/src/` |
| Go | `GoTddStrategy` | `.go` | `*_test.go` | `/src/`, `/cmd/`, `/pkg/`, `/internal/` |
| JavaScript/TypeScript | `JavaScriptTddStrategy` | `.js`, `.jsx`, `.ts`, `.tsx` | `*.test.*`, `*.spec.*` | `/src/`, `/lib/`, `/app/` |
| PHP | `PhpTddStrategy` | `.php` | `*Test.php` | `/src/`, `/app/` |
| Rust | `RustTddStrategy` | `.rs` | `*_test.rs` | `/src/` |
| Java | `JavaTddStrategy` | `.java` | `*Test.java` | `/src/main/` |
| C# | `CSharpTddStrategy` | `.cs` | `*Tests.cs`, `*Test.cs` | `/src/` |
| Kotlin | `KotlinTddStrategy` | `.kt` | `*Test.kt` | `/src/main/` |
| Ruby | `RubyTddStrategy` | `.rb` | `*_spec.rb`, `*_test.rb` | `/lib/`, `/app/` |
| Swift | `SwiftTddStrategy` | `.swift` | `*Tests.swift` | `/Sources/`, `/src/` |
| Dart | `DartTddStrategy` | `.dart` | `*_test.dart` | `/lib/` |
---
## Design Principles Applied
### SOLID
- **S** (Single Responsibility): Each strategy handles exactly one language. Handler handles only workflow. Registry handles only lookup. Common handles only shared utilities.
- **O** (Open/Closed): Adding a language = adding a file + one registry line. Zero changes to handler, protocol, common, or other strategies.
- **L** (Liskov Substitution): Every strategy is substitutable through the Protocol. Handler uses the same 7 members regardless of language.
- **I** (Interface Segregation): The Protocol has exactly 7 members - 2 properties (`language_name`, `extensions`) and 5 methods (`is_test_file`, `is_production_source`, `should_skip`, `compute_test_filename`, `get_acceptance_tests`) - no more. Every strategy implements all 7.
- **D** (Dependency Inversion): Handler depends on Protocol (abstraction), not on any concrete strategy class.
### DRY
- Common test directories defined once in `common.py`
- Directory matching logic defined once in `matches_directory()`
- Handler path mapping logic defined once (not per-language)
### NO MAGIC
- Every string literal is a named constant (`_LANGUAGE_NAME`, `_EXTENSIONS`, `_SOURCE_DIRECTORIES`, etc.)
- Module-level constants prefixed with `_` (private to module)
- Handler path constants also extracted (`_TEST_DIR`, `_TEST_UNIT_DIR`, `_SRC_DIR`)
### YAGNI
- No abstract base class (Protocol is sufficient)
- No config-driven strategy loading (create_default() is simple and explicit)
- No strategy caching (strategies are lightweight, stateless)
- No lazy loading (all strategies registered eagerly - they're cheap)
### FAIL FAST
- `get_strategy()` returns `None` for unknown extensions - handler allows through (no silent failure)
- `@runtime_checkable` Protocol enables isinstance validation if needed
- Missing strategy methods cause immediate `TypeError` at call site
---
## Anti-Patterns to Avoid
### DO NOT put language logic in the handler
```python
# WRONG - handler knows about Python
if file_path.endswith(".py"):
test_name = f"test_{filename}"
# RIGHT - handler delegates to strategy
test_name = strategy.compute_test_filename(filename)
```
### DO NOT use if/elif chains for languages
```python
# WRONG - God method with language awareness
if config.name == "python":
...
elif config.name == "go":
...
# RIGHT - Strategy lookup via registry
strategy = registry.get_strategy(file_path)
strategy.is_test_file(file_path)
```
### DO NOT import between strategies
```python
# WRONG - coupling between strategies
from claude_code_hooks_daemon.strategies.tdd.python_strategy import _SOURCE_DIRECTORIES
# RIGHT - use common.py for shared logic
from claude_code_hooks_daemon.strategies.tdd.common import matches_directory
```
### DO NOT use magic strings
```python
# WRONG
if "/src/" in file_path:
# RIGHT
_SOURCE_DIRECTORIES: tuple[str, ...] = ("/src/",)
return matches_directory(file_path, _SOURCE_DIRECTORIES)
```
### DO NOT inherit from a base class
```python
# WRONG - nominal typing, tight coupling
class PythonTddStrategy(BaseTddStrategy):
...
# RIGHT - structural typing, zero coupling
class PythonTddStrategy: # Just satisfies Protocol shape
...
```
---
## Applying This Pattern to Other Handlers
This TDD strategy is the archetype. Future language-aware handlers (e.g., linting enforcement, import validation, naming conventions) MUST follow the same structure:
1. **Define a Protocol** in `strategies/{domain}/protocol.py`
2. **Create shared utilities** in `strategies/{domain}/common.py`
3. **Implement one strategy per language** in `strategies/{domain}/{language}_strategy.py`
4. **Create a Registry** in `strategies/{domain}/registry.py`
5. **Keep the handler language-free** - delegate all language decisions to strategies
6. **TDD each strategy independently** with its own test file
7. **Document the pattern** with a `CLAUDE.md` in the strategy directory
### Example Future Domains
```
strategies/
├── tdd/ # THIS MODULE (archetype)
│ ├── CLAUDE.md
│ ├── protocol.py
│ ├── common.py
│ ├── registry.py
│ └── {language}_strategy.py (x11)
│
├── lint/ # Future: language-specific lint enforcement
│ ├── protocol.py (LintStrategy)
│ ├── common.py
│ ├── registry.py
│ └── {language}_strategy.py
│
├── naming/ # Future: naming convention enforcement
│ ├── protocol.py (NamingStrategy)
│ ├── common.py
│ ├── registry.py
│ └── {language}_strategy.py
│
└── imports/ # Future: import order/validation
├── protocol.py (ImportStrategy)
├── common.py
├── registry.py
└── {language}_strategy.py
```
---
## File Index
```
strategies/tdd/
├── CLAUDE.md # This document (archetype reference)
├── __init__.py # Public API: TddStrategy, TddStrategyRegistry
├── protocol.py # Strategy Protocol interface
├── common.py # Shared DRY utilities
├── registry.py # Extension-to-strategy mapping
├── python_strategy.py # Python strategy
├── go_strategy.py # Go strategy
├── javascript_strategy.py # JavaScript/TypeScript strategy
├── php_strategy.py # PHP strategy
├── rust_strategy.py # Rust strategy
├── java_strategy.py # Java strategy
├── csharp_strategy.py # C# strategy
├── kotlin_strategy.py # Kotlin strategy
├── ruby_strategy.py # Ruby strategy
├── swift_strategy.py # Swift strategy
└── dart_strategy.py # Dart strategy
tests/unit/strategies/tdd/
├── test_protocol.py # Protocol conformance tests
├── test_protocol_isinstance.py # @runtime_checkable isinstance() checks
├── test_common.py # Shared utility tests
├── test_tdd_strategy_registry.py # Registry tests
├── test_acceptance_tests.py # Acceptance test provision tests
├── test_python_strategy.py # Python strategy tests
├── test_go_strategy.py # Go strategy tests
├── test_javascript_strategy.py # JS/TS strategy tests
├── test_php_strategy.py # PHP strategy tests
├── test_rust_strategy.py # Rust strategy tests
├── test_java_strategy.py # Java strategy tests
├── test_csharp_strategy.py # C# strategy tests
├── test_kotlin_strategy.py # Kotlin strategy tests
├── test_ruby_strategy.py # Ruby strategy tests
├── test_swift_strategy.py # Swift strategy tests
└── test_dart_strategy.py # Dart strategy tests
```
---
**Pattern Source**: GoF Strategy Pattern adapted for Python Protocol typing
**Languages**: 11 (Python, Go, JS/TS, PHP, Rust, Java, C#, Kotlin, Ruby, Swift, Dart)
**Tests**: counts are deliberately not recorded here - a number frozen into a
document is stale the next time a test is added. Ask the test runner instead:
`pytest tests/unit/strategies/tdd/ tests/unit/handlers/test_tdd_enforcement.py --collect-only -q`