atdd 0.2.1__py3-none-any.whl
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.
- atdd/__init__.py +6 -0
- atdd/__main__.py +4 -0
- atdd/cli.py +404 -0
- atdd/coach/__init__.py +0 -0
- atdd/coach/commands/__init__.py +0 -0
- atdd/coach/commands/add_persistence_metadata.py +215 -0
- atdd/coach/commands/analyze_migrations.py +188 -0
- atdd/coach/commands/consumers.py +720 -0
- atdd/coach/commands/infer_governance_status.py +149 -0
- atdd/coach/commands/initializer.py +177 -0
- atdd/coach/commands/interface.py +1078 -0
- atdd/coach/commands/inventory.py +565 -0
- atdd/coach/commands/migration.py +240 -0
- atdd/coach/commands/registry.py +1560 -0
- atdd/coach/commands/session.py +430 -0
- atdd/coach/commands/sync.py +405 -0
- atdd/coach/commands/test_interface.py +399 -0
- atdd/coach/commands/test_runner.py +141 -0
- atdd/coach/commands/tests/__init__.py +1 -0
- atdd/coach/commands/tests/test_telemetry_array_validation.py +235 -0
- atdd/coach/commands/traceability.py +4264 -0
- atdd/coach/conventions/session.convention.yaml +754 -0
- atdd/coach/overlays/__init__.py +2 -0
- atdd/coach/overlays/claude.md +2 -0
- atdd/coach/schemas/config.schema.json +34 -0
- atdd/coach/schemas/manifest.schema.json +101 -0
- atdd/coach/templates/ATDD.md +282 -0
- atdd/coach/templates/SESSION-TEMPLATE.md +327 -0
- atdd/coach/utils/__init__.py +0 -0
- atdd/coach/utils/graph/__init__.py +0 -0
- atdd/coach/utils/graph/urn.py +875 -0
- atdd/coach/validators/__init__.py +0 -0
- atdd/coach/validators/shared_fixtures.py +365 -0
- atdd/coach/validators/test_enrich_wagon_registry.py +167 -0
- atdd/coach/validators/test_registry.py +575 -0
- atdd/coach/validators/test_session_validation.py +1183 -0
- atdd/coach/validators/test_traceability.py +448 -0
- atdd/coach/validators/test_update_feature_paths.py +108 -0
- atdd/coach/validators/test_validate_contract_consumers.py +297 -0
- atdd/coder/__init__.py +1 -0
- atdd/coder/conventions/adapter.recipe.yaml +88 -0
- atdd/coder/conventions/backend.convention.yaml +460 -0
- atdd/coder/conventions/boundaries.convention.yaml +666 -0
- atdd/coder/conventions/commons.convention.yaml +460 -0
- atdd/coder/conventions/complexity.recipe.yaml +109 -0
- atdd/coder/conventions/component-naming.convention.yaml +178 -0
- atdd/coder/conventions/design.convention.yaml +327 -0
- atdd/coder/conventions/design.recipe.yaml +273 -0
- atdd/coder/conventions/dto.convention.yaml +660 -0
- atdd/coder/conventions/frontend.convention.yaml +542 -0
- atdd/coder/conventions/green.convention.yaml +1012 -0
- atdd/coder/conventions/presentation.convention.yaml +587 -0
- atdd/coder/conventions/refactor.convention.yaml +535 -0
- atdd/coder/conventions/technology.convention.yaml +206 -0
- atdd/coder/conventions/tests/__init__.py +0 -0
- atdd/coder/conventions/tests/test_adapter_recipe.py +302 -0
- atdd/coder/conventions/tests/test_complexity_recipe.py +289 -0
- atdd/coder/conventions/tests/test_component_taxonomy.py +278 -0
- atdd/coder/conventions/tests/test_component_urn_naming.py +165 -0
- atdd/coder/conventions/tests/test_thinness_recipe.py +286 -0
- atdd/coder/conventions/thinness.recipe.yaml +82 -0
- atdd/coder/conventions/train.convention.yaml +325 -0
- atdd/coder/conventions/verification.protocol.yaml +53 -0
- atdd/coder/schemas/design_system.schema.json +361 -0
- atdd/coder/validators/__init__.py +0 -0
- atdd/coder/validators/test_commons_structure.py +485 -0
- atdd/coder/validators/test_complexity.py +416 -0
- atdd/coder/validators/test_cross_language_consistency.py +431 -0
- atdd/coder/validators/test_design_system_compliance.py +413 -0
- atdd/coder/validators/test_dto_testing_patterns.py +268 -0
- atdd/coder/validators/test_green_cross_stack_layers.py +168 -0
- atdd/coder/validators/test_green_layer_dependencies.py +148 -0
- atdd/coder/validators/test_green_python_layer_structure.py +103 -0
- atdd/coder/validators/test_green_supabase_layer_structure.py +103 -0
- atdd/coder/validators/test_import_boundaries.py +396 -0
- atdd/coder/validators/test_init_file_urns.py +593 -0
- atdd/coder/validators/test_preact_layer_boundaries.py +221 -0
- atdd/coder/validators/test_presentation_convention.py +260 -0
- atdd/coder/validators/test_python_architecture.py +674 -0
- atdd/coder/validators/test_quality_metrics.py +420 -0
- atdd/coder/validators/test_station_master_pattern.py +244 -0
- atdd/coder/validators/test_train_infrastructure.py +454 -0
- atdd/coder/validators/test_train_urns.py +293 -0
- atdd/coder/validators/test_typescript_architecture.py +616 -0
- atdd/coder/validators/test_usecase_structure.py +421 -0
- atdd/coder/validators/test_wagon_boundaries.py +586 -0
- atdd/conftest.py +126 -0
- atdd/planner/__init__.py +1 -0
- atdd/planner/conventions/acceptance.convention.yaml +538 -0
- atdd/planner/conventions/appendix.convention.yaml +187 -0
- atdd/planner/conventions/artifact-naming.convention.yaml +852 -0
- atdd/planner/conventions/component.convention.yaml +670 -0
- atdd/planner/conventions/criteria.convention.yaml +141 -0
- atdd/planner/conventions/feature.convention.yaml +371 -0
- atdd/planner/conventions/interface.convention.yaml +382 -0
- atdd/planner/conventions/steps.convention.yaml +141 -0
- atdd/planner/conventions/train.convention.yaml +552 -0
- atdd/planner/conventions/wagon.convention.yaml +275 -0
- atdd/planner/conventions/wmbt.convention.yaml +258 -0
- atdd/planner/schemas/acceptance.schema.json +336 -0
- atdd/planner/schemas/appendix.schema.json +78 -0
- atdd/planner/schemas/component.schema.json +114 -0
- atdd/planner/schemas/feature.schema.json +197 -0
- atdd/planner/schemas/train.schema.json +192 -0
- atdd/planner/schemas/wagon.schema.json +281 -0
- atdd/planner/schemas/wmbt.schema.json +59 -0
- atdd/planner/validators/__init__.py +0 -0
- atdd/planner/validators/conftest.py +5 -0
- atdd/planner/validators/test_draft_wagon_registry.py +374 -0
- atdd/planner/validators/test_plan_cross_refs.py +240 -0
- atdd/planner/validators/test_plan_uniqueness.py +224 -0
- atdd/planner/validators/test_plan_urn_resolution.py +268 -0
- atdd/planner/validators/test_plan_wagons.py +174 -0
- atdd/planner/validators/test_train_validation.py +514 -0
- atdd/planner/validators/test_wagon_urn_chain.py +648 -0
- atdd/planner/validators/test_wmbt_consistency.py +327 -0
- atdd/planner/validators/test_wmbt_vocabulary.py +632 -0
- atdd/tester/__init__.py +1 -0
- atdd/tester/conventions/artifact.convention.yaml +257 -0
- atdd/tester/conventions/contract.convention.yaml +1009 -0
- atdd/tester/conventions/filename.convention.yaml +555 -0
- atdd/tester/conventions/migration.convention.yaml +509 -0
- atdd/tester/conventions/red.convention.yaml +797 -0
- atdd/tester/conventions/routing.convention.yaml +51 -0
- atdd/tester/conventions/telemetry.convention.yaml +458 -0
- atdd/tester/schemas/a11y.tmpl.json +17 -0
- atdd/tester/schemas/artifact.schema.json +189 -0
- atdd/tester/schemas/contract.schema.json +591 -0
- atdd/tester/schemas/contract.tmpl.json +95 -0
- atdd/tester/schemas/db.tmpl.json +20 -0
- atdd/tester/schemas/e2e.tmpl.json +17 -0
- atdd/tester/schemas/edge_function.tmpl.json +17 -0
- atdd/tester/schemas/event.tmpl.json +17 -0
- atdd/tester/schemas/http.tmpl.json +19 -0
- atdd/tester/schemas/job.tmpl.json +18 -0
- atdd/tester/schemas/load.tmpl.json +21 -0
- atdd/tester/schemas/metric.tmpl.json +19 -0
- atdd/tester/schemas/pack.schema.json +139 -0
- atdd/tester/schemas/realtime.tmpl.json +20 -0
- atdd/tester/schemas/rls.tmpl.json +18 -0
- atdd/tester/schemas/script.tmpl.json +16 -0
- atdd/tester/schemas/sec.tmpl.json +18 -0
- atdd/tester/schemas/storage.tmpl.json +18 -0
- atdd/tester/schemas/telemetry.schema.json +128 -0
- atdd/tester/schemas/telemetry_tracking_manifest.schema.json +143 -0
- atdd/tester/schemas/test_filename.schema.json +194 -0
- atdd/tester/schemas/test_intent.schema.json +179 -0
- atdd/tester/schemas/unit.tmpl.json +18 -0
- atdd/tester/schemas/visual.tmpl.json +18 -0
- atdd/tester/schemas/ws.tmpl.json +17 -0
- atdd/tester/utils/__init__.py +0 -0
- atdd/tester/utils/filename.py +300 -0
- atdd/tester/validators/__init__.py +0 -0
- atdd/tester/validators/cleanup_duplicate_headers.py +116 -0
- atdd/tester/validators/cleanup_duplicate_headers_v2.py +135 -0
- atdd/tester/validators/conftest.py +5 -0
- atdd/tester/validators/coverage_gap_report.py +321 -0
- atdd/tester/validators/fix_dual_ac_references.py +179 -0
- atdd/tester/validators/remove_duplicate_lines.py +93 -0
- atdd/tester/validators/test_acceptance_urn_filename_mapping.py +359 -0
- atdd/tester/validators/test_acceptance_urn_separator.py +166 -0
- atdd/tester/validators/test_artifact_naming_category.py +307 -0
- atdd/tester/validators/test_contract_schema_compliance.py +706 -0
- atdd/tester/validators/test_contracts_structure.py +200 -0
- atdd/tester/validators/test_coverage_adequacy.py +797 -0
- atdd/tester/validators/test_dual_ac_reference.py +225 -0
- atdd/tester/validators/test_fixture_validity.py +372 -0
- atdd/tester/validators/test_isolation.py +487 -0
- atdd/tester/validators/test_migration_coverage.py +204 -0
- atdd/tester/validators/test_migration_criteria.py +276 -0
- atdd/tester/validators/test_migration_generation.py +116 -0
- atdd/tester/validators/test_python_test_naming.py +410 -0
- atdd/tester/validators/test_red_layer_validation.py +95 -0
- atdd/tester/validators/test_red_python_layer_structure.py +87 -0
- atdd/tester/validators/test_red_supabase_layer_structure.py +90 -0
- atdd/tester/validators/test_telemetry_structure.py +634 -0
- atdd/tester/validators/test_typescript_test_naming.py +301 -0
- atdd/tester/validators/test_typescript_test_structure.py +84 -0
- atdd-0.2.1.dist-info/METADATA +221 -0
- atdd-0.2.1.dist-info/RECORD +184 -0
- atdd-0.2.1.dist-info/WHEEL +5 -0
- atdd-0.2.1.dist-info/entry_points.txt +2 -0
- atdd-0.2.1.dist-info/licenses/LICENSE +674 -0
- atdd-0.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
schema_version: "1.0.1"
|
|
2
|
+
convention_id: "coder.refactor"
|
|
3
|
+
name: "REFACTOR Phase Convention"
|
|
4
|
+
description: "Transformation process and quality standards for refactoring GREEN code to clean architecture."
|
|
5
|
+
|
|
6
|
+
refactor_phase:
|
|
7
|
+
goal: "Preserve behavior; enforce architecture, clarity, and sustainability"
|
|
8
|
+
|
|
9
|
+
principles:
|
|
10
|
+
- id: RP-01
|
|
11
|
+
text: "No new behavior without new tests"
|
|
12
|
+
- id: RP-02
|
|
13
|
+
text: "No test changes except renames/moves"
|
|
14
|
+
- id: RP-03
|
|
15
|
+
text: "Improve internal structure until changes are cheap"
|
|
16
|
+
- id: RP-04
|
|
17
|
+
text: "Enforce dependency rules strictly"
|
|
18
|
+
|
|
19
|
+
architecture_target:
|
|
20
|
+
description: "Target 4-layer clean architecture (defined in code-type-specific conventions)"
|
|
21
|
+
conventions:
|
|
22
|
+
frontend: "conventions:coder:frontend"
|
|
23
|
+
backend: "conventions:coder:backend"
|
|
24
|
+
note: "Layer definitions, component types, file suffixes, and dependency rules defined in frontend/backend conventions"
|
|
25
|
+
|
|
26
|
+
# Composition Root Pattern (Maintained During Refactoring)
|
|
27
|
+
composition_root:
|
|
28
|
+
description: "composition.py and wagon.py survive refactoring unchanged - they orchestrate refactored components"
|
|
29
|
+
|
|
30
|
+
refactoring_approach: |
|
|
31
|
+
During REFACTOR, composition roots are the LAST files to change:
|
|
32
|
+
1. Refactor domain/application/integration/presentation layers
|
|
33
|
+
2. Update imports in composition.py/wagon.py to point to new locations
|
|
34
|
+
3. Composition logic stays the same (only import paths change)
|
|
35
|
+
|
|
36
|
+
hierarchy:
|
|
37
|
+
feature_level: "python/{wagon}/{feature}/composition.py"
|
|
38
|
+
wagon_level: "python/{wagon}/wagon.py"
|
|
39
|
+
train_level: "python/trains/runner.py (NEW in SESSION-12)"
|
|
40
|
+
application_level: "main.py or server.py (becomes Station Master)"
|
|
41
|
+
|
|
42
|
+
refactoring_workflow:
|
|
43
|
+
step_1:
|
|
44
|
+
action: "Refactor layers (domain, application, integration, presentation)"
|
|
45
|
+
note: "Move files to proper subdirectories, extract use cases, create ports"
|
|
46
|
+
|
|
47
|
+
step_2:
|
|
48
|
+
action: "Update composition.py imports to reflect new structure"
|
|
49
|
+
before: "from src.domain.timebank import Timebank"
|
|
50
|
+
after: "from src.domain.entities.timebank import Timebank"
|
|
51
|
+
|
|
52
|
+
step_3:
|
|
53
|
+
action: "Verify composition.py still works after refactoring"
|
|
54
|
+
command: "python3 python/{wagon}/{feature}/composition.py"
|
|
55
|
+
expectation: "Should run without errors"
|
|
56
|
+
|
|
57
|
+
step_4:
|
|
58
|
+
action: "Update wagon.py imports if features were restructured"
|
|
59
|
+
before: "from {wagon}.{feature}.src.domain.model import Model"
|
|
60
|
+
after: "from {wagon}.{feature}.src.domain.entities.model import Model"
|
|
61
|
+
|
|
62
|
+
step_5:
|
|
63
|
+
action: "Verify wagon.py orchestration after refactoring"
|
|
64
|
+
command: "python3 python/{wagon}/wagon.py"
|
|
65
|
+
expectation: "All features orchestrate correctly"
|
|
66
|
+
|
|
67
|
+
stability_principle: |
|
|
68
|
+
Composition roots provide STABILITY during refactoring:
|
|
69
|
+
- Features can be completely restructured internally
|
|
70
|
+
- As long as composition.py still works, external consumers are unaffected
|
|
71
|
+
- wagon.py provides integration regression testing during refactoring
|
|
72
|
+
- trains/runner.py orchestrates wagons for production user journeys (SESSION-12)
|
|
73
|
+
|
|
74
|
+
testing_strategy:
|
|
75
|
+
unit_tests: "Test refactored domain/application layers"
|
|
76
|
+
integration_tests: "Test via feature composition.py"
|
|
77
|
+
wagon_tests: "Test via wagon.py orchestration"
|
|
78
|
+
acceptance_tests: "Still GREEN throughout refactoring"
|
|
79
|
+
|
|
80
|
+
rich_cli_feedback:
|
|
81
|
+
description: "wagon.py should provide rich visual feedback during orchestration"
|
|
82
|
+
rationale: |
|
|
83
|
+
wagon.py is a manual test harness and demo tool.
|
|
84
|
+
Rich CLI feedback makes it:
|
|
85
|
+
- More engaging for stakeholder demos
|
|
86
|
+
- Easier to understand mechanic behavior
|
|
87
|
+
- Better for debugging and manual testing
|
|
88
|
+
- More professional and polished
|
|
89
|
+
|
|
90
|
+
standard_patterns:
|
|
91
|
+
real_time_simulation:
|
|
92
|
+
description: "Add delays to simulate real-world timing"
|
|
93
|
+
examples:
|
|
94
|
+
- "Animated dots during processing: 'Turn 1: ...'"
|
|
95
|
+
- "Small delays between steps (0.3s)"
|
|
96
|
+
- "Simulates user thinking time"
|
|
97
|
+
|
|
98
|
+
progress_visualization:
|
|
99
|
+
description: "Visual progress bars for time/resource depletion"
|
|
100
|
+
examples:
|
|
101
|
+
- "Color-coded progress bars (green/yellow/red)"
|
|
102
|
+
- "Percentage and absolute values shown"
|
|
103
|
+
- "Example: [████████░░░░] 80.0s / 100.0s"
|
|
104
|
+
|
|
105
|
+
status_indicators:
|
|
106
|
+
description: "Clear visual status with emojis and colors"
|
|
107
|
+
examples:
|
|
108
|
+
- "✅ Success indicators"
|
|
109
|
+
- "⚠️ Warning states"
|
|
110
|
+
- "🔴 Error/critical states"
|
|
111
|
+
- "📊 Status summaries"
|
|
112
|
+
|
|
113
|
+
structured_output:
|
|
114
|
+
description: "Organized output with headers and sections"
|
|
115
|
+
examples:
|
|
116
|
+
- "Section headers: ========"
|
|
117
|
+
- "Subsections with indentation"
|
|
118
|
+
- "Summary tables at end"
|
|
119
|
+
|
|
120
|
+
implementation_example: |
|
|
121
|
+
# Real-time simulation with progress bar
|
|
122
|
+
def show_progress_bar(current: float, total: float, width: int = 40):
|
|
123
|
+
percentage = current / total
|
|
124
|
+
filled = int(width * percentage)
|
|
125
|
+
bar = "█" * filled + "░" * (width - filled)
|
|
126
|
+
|
|
127
|
+
# Color coding
|
|
128
|
+
if percentage > 0.5:
|
|
129
|
+
color = "\033[92m" # Green
|
|
130
|
+
elif percentage > 0.2:
|
|
131
|
+
color = "\033[93m" # Yellow
|
|
132
|
+
else:
|
|
133
|
+
color = "\033[91m" # Red
|
|
134
|
+
reset = "\033[0m"
|
|
135
|
+
|
|
136
|
+
return f"{color}[{bar}]{reset} {current:.1f} / {total:.1f}"
|
|
137
|
+
|
|
138
|
+
# Animated processing
|
|
139
|
+
for turn in turns:
|
|
140
|
+
print(f" Turn {i}: ", end="", flush=True)
|
|
141
|
+
for _ in range(3):
|
|
142
|
+
print(".", end="", flush=True)
|
|
143
|
+
time.sleep(0.15)
|
|
144
|
+
print(f" {turn:.1f}s")
|
|
145
|
+
print(f" {show_progress_bar(remaining, total)}")
|
|
146
|
+
time.sleep(0.3)
|
|
147
|
+
|
|
148
|
+
when_to_add:
|
|
149
|
+
timing: "During REFACTOR phase (after GREEN is complete)"
|
|
150
|
+
scenarios:
|
|
151
|
+
- "Wagon orchestrates multiple features"
|
|
152
|
+
- "Time-based or resource-based mechanics"
|
|
153
|
+
- "Need for stakeholder demos"
|
|
154
|
+
- "Manual testing of complex flows"
|
|
155
|
+
not_needed:
|
|
156
|
+
- "Simple wagons with instant operations"
|
|
157
|
+
- "Pure library/utility wagons"
|
|
158
|
+
- "Wagons only used in automated tests"
|
|
159
|
+
|
|
160
|
+
actions:
|
|
161
|
+
- id: RF-DOMAIN
|
|
162
|
+
action: "Extract/move logic to Domain layer; keep it pure"
|
|
163
|
+
checklist:
|
|
164
|
+
- "Identify business rules currently in handlers/services"
|
|
165
|
+
- "Extract to pure functions/classes with no dependencies"
|
|
166
|
+
- "Create value objects for primitives (Email, Money, OrderId)"
|
|
167
|
+
- "Create entities with identity and lifecycle"
|
|
168
|
+
- "Move invariant validation into domain models"
|
|
169
|
+
verification:
|
|
170
|
+
- "Domain tests run without any infrastructure"
|
|
171
|
+
- "No imports from application/presentation/integration"
|
|
172
|
+
pattern: |
|
|
173
|
+
// Domain layer - Pure business logic
|
|
174
|
+
class Order {
|
|
175
|
+
private constructor(
|
|
176
|
+
public readonly id: OrderId,
|
|
177
|
+
public readonly items: OrderItem[],
|
|
178
|
+
public readonly total: Money
|
|
179
|
+
) {}
|
|
180
|
+
|
|
181
|
+
static create(data: CreateOrderData): Order {
|
|
182
|
+
if (data.items.length === 0) {
|
|
183
|
+
throw new DomainException('Order must have at least one item')
|
|
184
|
+
}
|
|
185
|
+
// All business rules validated here
|
|
186
|
+
return new Order(...)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
canBeCancelled(): boolean {
|
|
190
|
+
return this.status === 'pending' // Pure business logic
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
checks:
|
|
194
|
+
- type: import_scan
|
|
195
|
+
path: "domain/**"
|
|
196
|
+
forbid:
|
|
197
|
+
- "domain → application"
|
|
198
|
+
- "domain → presentation"
|
|
199
|
+
- "domain → integration"
|
|
200
|
+
- type: grep
|
|
201
|
+
path: "domain/**"
|
|
202
|
+
must_not_match:
|
|
203
|
+
- "import.*from.*['\"].*/(application|presentation|integration)"
|
|
204
|
+
|
|
205
|
+
- id: RF-USECASE
|
|
206
|
+
action: "Create Application use cases; inject ports"
|
|
207
|
+
checklist:
|
|
208
|
+
- "Define port interfaces for all I/O operations"
|
|
209
|
+
- "Create use case classes that orchestrate domain + ports"
|
|
210
|
+
- "Inject ports via constructor"
|
|
211
|
+
- "Keep use cases framework-agnostic"
|
|
212
|
+
pattern: |
|
|
213
|
+
// Application layer
|
|
214
|
+
interface OrderRepository { // Port
|
|
215
|
+
save(order: Order): Promise<void>
|
|
216
|
+
findById(id: OrderId): Promise<Order>
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
class CreateOrderUseCase {
|
|
220
|
+
constructor(
|
|
221
|
+
private orderRepo: OrderRepository, // Port injection
|
|
222
|
+
private paymentGateway: PaymentGateway
|
|
223
|
+
) {}
|
|
224
|
+
|
|
225
|
+
async execute(data: CreateOrderInput): Promise<Order> {
|
|
226
|
+
const order = Order.create(data) // Domain logic
|
|
227
|
+
await this.paymentGateway.charge(order.total)
|
|
228
|
+
await this.orderRepo.save(order)
|
|
229
|
+
return order
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
checks:
|
|
233
|
+
- type: import_scan
|
|
234
|
+
path: "application/**"
|
|
235
|
+
forbid:
|
|
236
|
+
- "application → presentation"
|
|
237
|
+
- "application → integration (except port interfaces)"
|
|
238
|
+
|
|
239
|
+
- id: RF-PRESENTATION
|
|
240
|
+
action: "Extract presentation layer; handlers become thin"
|
|
241
|
+
checklist:
|
|
242
|
+
- "Move all handlers to presentation/"
|
|
243
|
+
- "Handlers only: parse request → call use case → format response"
|
|
244
|
+
- "Move DTOs/validators to presentation/"
|
|
245
|
+
- "No business logic in handlers"
|
|
246
|
+
pattern: |
|
|
247
|
+
// Presentation layer
|
|
248
|
+
async function handleCreateOrder(req: Request): Promise<Response> {
|
|
249
|
+
// 1. Parse & validate
|
|
250
|
+
const input = CreateOrderDTO.parse(req.body)
|
|
251
|
+
|
|
252
|
+
// 2. Call use case
|
|
253
|
+
const order = await createOrderUseCase.execute(input)
|
|
254
|
+
|
|
255
|
+
// 3. Format response
|
|
256
|
+
return { status: 201, body: OrderResponseDTO.from(order) }
|
|
257
|
+
}
|
|
258
|
+
checks:
|
|
259
|
+
- type: grep
|
|
260
|
+
path: "presentation/**"
|
|
261
|
+
must_not_match:
|
|
262
|
+
- "new PgClient\\("
|
|
263
|
+
- "new Pool\\("
|
|
264
|
+
- "axios\\."
|
|
265
|
+
- "fetch\\("
|
|
266
|
+
- "fs\\."
|
|
267
|
+
|
|
268
|
+
- id: RF-INFRA
|
|
269
|
+
action: "Implement ports in Integration layer; remove direct calls"
|
|
270
|
+
checklist:
|
|
271
|
+
- "Create concrete implementations of all ports"
|
|
272
|
+
- "Move DB/HTTP/file code to integration/"
|
|
273
|
+
- "Wire implementations via DI/factory"
|
|
274
|
+
- "Replace in-memory fakes with real adapters"
|
|
275
|
+
pattern: |
|
|
276
|
+
// Integration layer
|
|
277
|
+
class PostgresOrderRepository implements OrderRepository {
|
|
278
|
+
constructor(private db: Database) {}
|
|
279
|
+
|
|
280
|
+
async save(order: Order): Promise<void> {
|
|
281
|
+
const row = OrderMapper.toRow(order) // Map domain → DB
|
|
282
|
+
await this.db.insert('orders', row)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async findById(id: OrderId): Promise<Order> {
|
|
286
|
+
const row = await this.db.findOne('orders', { id: id.value })
|
|
287
|
+
return OrderMapper.toDomain(row) // Map DB → domain
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
checks:
|
|
291
|
+
- type: reference
|
|
292
|
+
note: "Verify every port interface has at least one adapter implementation"
|
|
293
|
+
|
|
294
|
+
- id: RF-SPLIT
|
|
295
|
+
action: "Split large functions; remove duplication; name precisely"
|
|
296
|
+
guidelines:
|
|
297
|
+
- "Functions < 20 lines"
|
|
298
|
+
- "Cyclomatic complexity < 10"
|
|
299
|
+
- "Extract helper functions"
|
|
300
|
+
- "Use descriptive names (no abbreviations)"
|
|
301
|
+
- "Apply DRY after 3rd occurrence"
|
|
302
|
+
checks:
|
|
303
|
+
- type: complexity_scan
|
|
304
|
+
max_cyclomatic: 10
|
|
305
|
+
max_function_lines: 30
|
|
306
|
+
|
|
307
|
+
- id: RF-ARCHTESTS
|
|
308
|
+
action: "Add boundary tests/lint to enforce layers"
|
|
309
|
+
examples:
|
|
310
|
+
- "Domain layer imports nothing"
|
|
311
|
+
- "Application layer doesn't import presentation/integration"
|
|
312
|
+
- "Integration implements all defined ports"
|
|
313
|
+
- "No cross-feature imports"
|
|
314
|
+
tools:
|
|
315
|
+
- "ESLint + eslint-plugin-boundaries"
|
|
316
|
+
- "ArchUnit (Java/Kotlin)"
|
|
317
|
+
- "dependency-cruiser (TypeScript)"
|
|
318
|
+
- "Custom import analyzer"
|
|
319
|
+
|
|
320
|
+
- id: RF-FAKES
|
|
321
|
+
action: "Replace in-memory fakes with real adapters (keep fakes for tests)"
|
|
322
|
+
strategy:
|
|
323
|
+
- "Production: wire real adapters (PostgresOrderRepo)"
|
|
324
|
+
- "Tests: keep using in-memory fakes (InMemoryOrderRepo)"
|
|
325
|
+
- "Both implement same port interface"
|
|
326
|
+
|
|
327
|
+
- id: RF-CROSSCUT
|
|
328
|
+
action: "Add logging/telemetry at edges; handle errors centrally"
|
|
329
|
+
patterns:
|
|
330
|
+
- "Middleware for request logging"
|
|
331
|
+
- "Centralized error handler"
|
|
332
|
+
- "Use case decorators for tracing"
|
|
333
|
+
- "Domain events for audit logs"
|
|
334
|
+
|
|
335
|
+
non_functional:
|
|
336
|
+
- id: NF-VALIDATION
|
|
337
|
+
requirement: "Centralized at Presentation/Application boundary"
|
|
338
|
+
pattern: |
|
|
339
|
+
// Presentation: syntax validation
|
|
340
|
+
class CreateOrderDTO {
|
|
341
|
+
@IsString() customerId: string
|
|
342
|
+
@IsArray() items: OrderItemDTO[]
|
|
343
|
+
|
|
344
|
+
static parse(raw: unknown): CreateOrderDTO {
|
|
345
|
+
// Validate structure & types
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Domain: semantic validation
|
|
350
|
+
class Order {
|
|
351
|
+
static create(data: CreateOrderInput): Order {
|
|
352
|
+
if (data.items.length === 0) {
|
|
353
|
+
throw new DomainException('Order must have at least one item')
|
|
354
|
+
}
|
|
355
|
+
// Business rules validated here
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
- id: NF-CONFIG
|
|
360
|
+
requirement: "Config via env; secrets via provider; no hardcoded creds"
|
|
361
|
+
pattern: |
|
|
362
|
+
// Integration layer
|
|
363
|
+
class DatabaseConfig {
|
|
364
|
+
static fromEnv(): DatabaseConfig {
|
|
365
|
+
return {
|
|
366
|
+
host: process.env.DB_HOST,
|
|
367
|
+
password: secretsManager.get('DB_PASSWORD') // Not hardcoded
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
- id: NF-PERF
|
|
373
|
+
requirement: "Only optimize where profiling shows need"
|
|
374
|
+
approach:
|
|
375
|
+
- "Profile first"
|
|
376
|
+
- "Optimize hot paths only"
|
|
377
|
+
- "Add caching strategically"
|
|
378
|
+
- "Don't sacrifice clarity for premature optimization"
|
|
379
|
+
|
|
380
|
+
- id: NF-ERRORS
|
|
381
|
+
requirement: "Domain errors vs infrastructure errors"
|
|
382
|
+
pattern: |
|
|
383
|
+
// Domain errors (expected)
|
|
384
|
+
class OrderAlreadyShippedException extends DomainException {}
|
|
385
|
+
|
|
386
|
+
// Infrastructure errors (unexpected)
|
|
387
|
+
class DatabaseConnectionError extends InfrastructureException {}
|
|
388
|
+
|
|
389
|
+
// Presentation: map to HTTP status
|
|
390
|
+
function errorHandler(error: Error): Response {
|
|
391
|
+
if (error instanceof DomainException) return { status: 400 }
|
|
392
|
+
if (error instanceof InfrastructureException) return { status: 500 }
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
deliverables:
|
|
396
|
+
- id: DL-STRUCTURE
|
|
397
|
+
rule: "4-layer folder structure enforced per code-type convention"
|
|
398
|
+
reference: "See frontend.convention.yaml or backend.convention.yaml for specific component types and file suffixes"
|
|
399
|
+
|
|
400
|
+
- id: DL-PORTS
|
|
401
|
+
rule: "Port interfaces in application/; adapters in integration/"
|
|
402
|
+
verification:
|
|
403
|
+
- "Every port has at least one implementation"
|
|
404
|
+
- "Implementations injected via DI/factory"
|
|
405
|
+
- "Tests can swap implementations easily"
|
|
406
|
+
|
|
407
|
+
- id: DL-GUARDS
|
|
408
|
+
rule: "Architecture guardrails active in CI"
|
|
409
|
+
examples:
|
|
410
|
+
- "ESLint rules block forbidden imports"
|
|
411
|
+
- "CI fails if domain imports application"
|
|
412
|
+
- "ArchUnit tests verify layer boundaries"
|
|
413
|
+
checks:
|
|
414
|
+
- type: import_scan
|
|
415
|
+
forbid:
|
|
416
|
+
- "domain → application|presentation|integration"
|
|
417
|
+
- "application → presentation|integration"
|
|
418
|
+
- "presentation → integration"
|
|
419
|
+
|
|
420
|
+
- id: DL-TESTS
|
|
421
|
+
rule: "Internal unit tests for Domain & Application; acceptance tests still green"
|
|
422
|
+
targets:
|
|
423
|
+
domain_coverage: ">=90%"
|
|
424
|
+
application_coverage: ">=80%"
|
|
425
|
+
integration_coverage: ">=70%"
|
|
426
|
+
note: "Presentation layer covered by acceptance tests"
|
|
427
|
+
|
|
428
|
+
- id: DL-LOG
|
|
429
|
+
rule: "Short log documenting moves and remaining debt"
|
|
430
|
+
template: |
|
|
431
|
+
## Refactoring Summary
|
|
432
|
+
|
|
433
|
+
### Extracted to Domain
|
|
434
|
+
- Order entity with validation
|
|
435
|
+
- Money value object
|
|
436
|
+
- OrderPolicy business rules
|
|
437
|
+
|
|
438
|
+
### Created Use Cases
|
|
439
|
+
- CreateOrderUseCase
|
|
440
|
+
- CancelOrderUseCase
|
|
441
|
+
|
|
442
|
+
### Ports Defined
|
|
443
|
+
- OrderRepository (implemented by PostgresOrderRepo)
|
|
444
|
+
- PaymentGateway (implemented by StripePaymentGateway)
|
|
445
|
+
|
|
446
|
+
### Remaining Tech Debt
|
|
447
|
+
- TODO: Add caching layer for frequent queries
|
|
448
|
+
- TODO: Implement outbox pattern for event publishing
|
|
449
|
+
- TODO: Add circuit breaker to payment gateway
|
|
450
|
+
|
|
451
|
+
done_criteria:
|
|
452
|
+
- id: DC-TESTS
|
|
453
|
+
requirement: "All acceptance tests still pass (no behavior change)"
|
|
454
|
+
- id: DC-ARCH
|
|
455
|
+
requirement: "Layers compile without upward leaks (no infra import in domain/app)"
|
|
456
|
+
- id: DC-COMPLEXITY
|
|
457
|
+
requirement: "Cyclomatic complexity and duplication reduced"
|
|
458
|
+
- id: DC-SEAMS
|
|
459
|
+
requirement: "Clear seams for future changes"
|
|
460
|
+
- id: DC-MAINT
|
|
461
|
+
requirement: "New features can be added with minimal changes"
|
|
462
|
+
|
|
463
|
+
anti_patterns:
|
|
464
|
+
- id: AP-LEAK
|
|
465
|
+
text: "Leaky abstractions"
|
|
466
|
+
avoid: "Domain/application importing integration types"
|
|
467
|
+
example: "Order entity importing PostgreSQL types"
|
|
468
|
+
fix: "Use mappers in integration layer"
|
|
469
|
+
|
|
470
|
+
- id: AP-ANEMIC
|
|
471
|
+
text: "Anemic domain model"
|
|
472
|
+
avoid: "Entities with only getters/setters; all logic in services"
|
|
473
|
+
fix: "Move business rules into entity methods"
|
|
474
|
+
|
|
475
|
+
- id: AP-FATUC
|
|
476
|
+
text: "Fat use cases"
|
|
477
|
+
avoid: "Use cases with complex business logic"
|
|
478
|
+
fix: "Extract domain services or move logic to entities"
|
|
479
|
+
|
|
480
|
+
- id: AP-SKIPPORTS
|
|
481
|
+
text: "Skipping ports pattern"
|
|
482
|
+
avoid: "Presentation calling integration directly"
|
|
483
|
+
fix: "Always route through application use cases"
|
|
484
|
+
|
|
485
|
+
- id: AP-OVERENG
|
|
486
|
+
text: "Over-engineering"
|
|
487
|
+
avoid: "Adding patterns before they're needed"
|
|
488
|
+
fix: "Wait for 3rd occurrence before abstracting"
|
|
489
|
+
|
|
490
|
+
quality_metrics:
|
|
491
|
+
- id: QM-ARCH
|
|
492
|
+
metrics:
|
|
493
|
+
- "Zero forbidden imports detected"
|
|
494
|
+
- "All ports have adapters"
|
|
495
|
+
- "Domain layer has zero external dependencies"
|
|
496
|
+
checks:
|
|
497
|
+
- type: import_scan
|
|
498
|
+
report: "forbidden_edges_count"
|
|
499
|
+
|
|
500
|
+
- id: QM-COMPLEXITY
|
|
501
|
+
metrics:
|
|
502
|
+
- "Average cyclomatic complexity < 5"
|
|
503
|
+
- "Max function length < 30 lines"
|
|
504
|
+
- "Max class length < 200 lines"
|
|
505
|
+
|
|
506
|
+
- id: QM-DUP
|
|
507
|
+
metrics:
|
|
508
|
+
- "< 5% code duplication"
|
|
509
|
+
- "Similar logic extracted to shared helpers"
|
|
510
|
+
|
|
511
|
+
- id: QM-TEST
|
|
512
|
+
metrics:
|
|
513
|
+
- "Domain: 100% testable without infrastructure"
|
|
514
|
+
- "Application: 100% testable with mocked ports"
|
|
515
|
+
- "Integration: Testable with test containers/fakes"
|
|
516
|
+
|
|
517
|
+
ci_gates:
|
|
518
|
+
description: "Automated enforcement in CI pipeline"
|
|
519
|
+
on: "pull_request"
|
|
520
|
+
require:
|
|
521
|
+
- "acceptance: green"
|
|
522
|
+
- DL-GUARDS
|
|
523
|
+
- QM-ARCH
|
|
524
|
+
thresholds:
|
|
525
|
+
complexity: "avg < 5"
|
|
526
|
+
coverage_domain: ">=90%"
|
|
527
|
+
coverage_application: ">=80%"
|
|
528
|
+
|
|
529
|
+
handoff_criteria:
|
|
530
|
+
description: "Ready for next feature when all criteria met"
|
|
531
|
+
checklist:
|
|
532
|
+
- "All GREEN TODOs addressed"
|
|
533
|
+
- "Boundary tests passing"
|
|
534
|
+
- "Refactor log committed"
|
|
535
|
+
- "Ready for next feature"
|