atdd 0.1.0__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.
Files changed (183) hide show
  1. atdd/__init__.py +0 -0
  2. atdd/cli.py +404 -0
  3. atdd/coach/__init__.py +0 -0
  4. atdd/coach/commands/__init__.py +0 -0
  5. atdd/coach/commands/add_persistence_metadata.py +215 -0
  6. atdd/coach/commands/analyze_migrations.py +188 -0
  7. atdd/coach/commands/consumers.py +720 -0
  8. atdd/coach/commands/infer_governance_status.py +149 -0
  9. atdd/coach/commands/initializer.py +177 -0
  10. atdd/coach/commands/interface.py +1078 -0
  11. atdd/coach/commands/inventory.py +565 -0
  12. atdd/coach/commands/migration.py +240 -0
  13. atdd/coach/commands/registry.py +1560 -0
  14. atdd/coach/commands/session.py +430 -0
  15. atdd/coach/commands/sync.py +405 -0
  16. atdd/coach/commands/test_interface.py +399 -0
  17. atdd/coach/commands/test_runner.py +141 -0
  18. atdd/coach/commands/tests/__init__.py +1 -0
  19. atdd/coach/commands/tests/test_telemetry_array_validation.py +235 -0
  20. atdd/coach/commands/traceability.py +4264 -0
  21. atdd/coach/conventions/session.convention.yaml +754 -0
  22. atdd/coach/overlays/__init__.py +2 -0
  23. atdd/coach/overlays/claude.md +2 -0
  24. atdd/coach/schemas/config.schema.json +34 -0
  25. atdd/coach/schemas/manifest.schema.json +101 -0
  26. atdd/coach/templates/ATDD.md +282 -0
  27. atdd/coach/templates/SESSION-TEMPLATE.md +327 -0
  28. atdd/coach/utils/__init__.py +0 -0
  29. atdd/coach/utils/graph/__init__.py +0 -0
  30. atdd/coach/utils/graph/urn.py +875 -0
  31. atdd/coach/validators/__init__.py +0 -0
  32. atdd/coach/validators/shared_fixtures.py +365 -0
  33. atdd/coach/validators/test_enrich_wagon_registry.py +167 -0
  34. atdd/coach/validators/test_registry.py +575 -0
  35. atdd/coach/validators/test_session_validation.py +1183 -0
  36. atdd/coach/validators/test_traceability.py +448 -0
  37. atdd/coach/validators/test_update_feature_paths.py +108 -0
  38. atdd/coach/validators/test_validate_contract_consumers.py +297 -0
  39. atdd/coder/__init__.py +1 -0
  40. atdd/coder/conventions/adapter.recipe.yaml +88 -0
  41. atdd/coder/conventions/backend.convention.yaml +460 -0
  42. atdd/coder/conventions/boundaries.convention.yaml +666 -0
  43. atdd/coder/conventions/commons.convention.yaml +460 -0
  44. atdd/coder/conventions/complexity.recipe.yaml +109 -0
  45. atdd/coder/conventions/component-naming.convention.yaml +178 -0
  46. atdd/coder/conventions/design.convention.yaml +327 -0
  47. atdd/coder/conventions/design.recipe.yaml +273 -0
  48. atdd/coder/conventions/dto.convention.yaml +660 -0
  49. atdd/coder/conventions/frontend.convention.yaml +542 -0
  50. atdd/coder/conventions/green.convention.yaml +1012 -0
  51. atdd/coder/conventions/presentation.convention.yaml +587 -0
  52. atdd/coder/conventions/refactor.convention.yaml +535 -0
  53. atdd/coder/conventions/technology.convention.yaml +206 -0
  54. atdd/coder/conventions/tests/__init__.py +0 -0
  55. atdd/coder/conventions/tests/test_adapter_recipe.py +302 -0
  56. atdd/coder/conventions/tests/test_complexity_recipe.py +289 -0
  57. atdd/coder/conventions/tests/test_component_taxonomy.py +278 -0
  58. atdd/coder/conventions/tests/test_component_urn_naming.py +165 -0
  59. atdd/coder/conventions/tests/test_thinness_recipe.py +286 -0
  60. atdd/coder/conventions/thinness.recipe.yaml +82 -0
  61. atdd/coder/conventions/train.convention.yaml +325 -0
  62. atdd/coder/conventions/verification.protocol.yaml +53 -0
  63. atdd/coder/schemas/design_system.schema.json +361 -0
  64. atdd/coder/validators/__init__.py +0 -0
  65. atdd/coder/validators/test_commons_structure.py +485 -0
  66. atdd/coder/validators/test_complexity.py +416 -0
  67. atdd/coder/validators/test_cross_language_consistency.py +431 -0
  68. atdd/coder/validators/test_design_system_compliance.py +413 -0
  69. atdd/coder/validators/test_dto_testing_patterns.py +268 -0
  70. atdd/coder/validators/test_green_cross_stack_layers.py +168 -0
  71. atdd/coder/validators/test_green_layer_dependencies.py +148 -0
  72. atdd/coder/validators/test_green_python_layer_structure.py +103 -0
  73. atdd/coder/validators/test_green_supabase_layer_structure.py +103 -0
  74. atdd/coder/validators/test_import_boundaries.py +396 -0
  75. atdd/coder/validators/test_init_file_urns.py +593 -0
  76. atdd/coder/validators/test_preact_layer_boundaries.py +221 -0
  77. atdd/coder/validators/test_presentation_convention.py +260 -0
  78. atdd/coder/validators/test_python_architecture.py +674 -0
  79. atdd/coder/validators/test_quality_metrics.py +420 -0
  80. atdd/coder/validators/test_station_master_pattern.py +244 -0
  81. atdd/coder/validators/test_train_infrastructure.py +454 -0
  82. atdd/coder/validators/test_train_urns.py +293 -0
  83. atdd/coder/validators/test_typescript_architecture.py +616 -0
  84. atdd/coder/validators/test_usecase_structure.py +421 -0
  85. atdd/coder/validators/test_wagon_boundaries.py +586 -0
  86. atdd/conftest.py +126 -0
  87. atdd/planner/__init__.py +1 -0
  88. atdd/planner/conventions/acceptance.convention.yaml +538 -0
  89. atdd/planner/conventions/appendix.convention.yaml +187 -0
  90. atdd/planner/conventions/artifact-naming.convention.yaml +852 -0
  91. atdd/planner/conventions/component.convention.yaml +670 -0
  92. atdd/planner/conventions/criteria.convention.yaml +141 -0
  93. atdd/planner/conventions/feature.convention.yaml +371 -0
  94. atdd/planner/conventions/interface.convention.yaml +382 -0
  95. atdd/planner/conventions/steps.convention.yaml +141 -0
  96. atdd/planner/conventions/train.convention.yaml +552 -0
  97. atdd/planner/conventions/wagon.convention.yaml +275 -0
  98. atdd/planner/conventions/wmbt.convention.yaml +258 -0
  99. atdd/planner/schemas/acceptance.schema.json +336 -0
  100. atdd/planner/schemas/appendix.schema.json +78 -0
  101. atdd/planner/schemas/component.schema.json +114 -0
  102. atdd/planner/schemas/feature.schema.json +197 -0
  103. atdd/planner/schemas/train.schema.json +192 -0
  104. atdd/planner/schemas/wagon.schema.json +281 -0
  105. atdd/planner/schemas/wmbt.schema.json +59 -0
  106. atdd/planner/validators/__init__.py +0 -0
  107. atdd/planner/validators/conftest.py +5 -0
  108. atdd/planner/validators/test_draft_wagon_registry.py +374 -0
  109. atdd/planner/validators/test_plan_cross_refs.py +240 -0
  110. atdd/planner/validators/test_plan_uniqueness.py +224 -0
  111. atdd/planner/validators/test_plan_urn_resolution.py +268 -0
  112. atdd/planner/validators/test_plan_wagons.py +174 -0
  113. atdd/planner/validators/test_train_validation.py +514 -0
  114. atdd/planner/validators/test_wagon_urn_chain.py +648 -0
  115. atdd/planner/validators/test_wmbt_consistency.py +327 -0
  116. atdd/planner/validators/test_wmbt_vocabulary.py +632 -0
  117. atdd/tester/__init__.py +1 -0
  118. atdd/tester/conventions/artifact.convention.yaml +257 -0
  119. atdd/tester/conventions/contract.convention.yaml +1009 -0
  120. atdd/tester/conventions/filename.convention.yaml +555 -0
  121. atdd/tester/conventions/migration.convention.yaml +509 -0
  122. atdd/tester/conventions/red.convention.yaml +797 -0
  123. atdd/tester/conventions/routing.convention.yaml +51 -0
  124. atdd/tester/conventions/telemetry.convention.yaml +458 -0
  125. atdd/tester/schemas/a11y.tmpl.json +17 -0
  126. atdd/tester/schemas/artifact.schema.json +189 -0
  127. atdd/tester/schemas/contract.schema.json +591 -0
  128. atdd/tester/schemas/contract.tmpl.json +95 -0
  129. atdd/tester/schemas/db.tmpl.json +20 -0
  130. atdd/tester/schemas/e2e.tmpl.json +17 -0
  131. atdd/tester/schemas/edge_function.tmpl.json +17 -0
  132. atdd/tester/schemas/event.tmpl.json +17 -0
  133. atdd/tester/schemas/http.tmpl.json +19 -0
  134. atdd/tester/schemas/job.tmpl.json +18 -0
  135. atdd/tester/schemas/load.tmpl.json +21 -0
  136. atdd/tester/schemas/metric.tmpl.json +19 -0
  137. atdd/tester/schemas/pack.schema.json +139 -0
  138. atdd/tester/schemas/realtime.tmpl.json +20 -0
  139. atdd/tester/schemas/rls.tmpl.json +18 -0
  140. atdd/tester/schemas/script.tmpl.json +16 -0
  141. atdd/tester/schemas/sec.tmpl.json +18 -0
  142. atdd/tester/schemas/storage.tmpl.json +18 -0
  143. atdd/tester/schemas/telemetry.schema.json +128 -0
  144. atdd/tester/schemas/telemetry_tracking_manifest.schema.json +143 -0
  145. atdd/tester/schemas/test_filename.schema.json +194 -0
  146. atdd/tester/schemas/test_intent.schema.json +179 -0
  147. atdd/tester/schemas/unit.tmpl.json +18 -0
  148. atdd/tester/schemas/visual.tmpl.json +18 -0
  149. atdd/tester/schemas/ws.tmpl.json +17 -0
  150. atdd/tester/utils/__init__.py +0 -0
  151. atdd/tester/utils/filename.py +300 -0
  152. atdd/tester/validators/__init__.py +0 -0
  153. atdd/tester/validators/cleanup_duplicate_headers.py +116 -0
  154. atdd/tester/validators/cleanup_duplicate_headers_v2.py +135 -0
  155. atdd/tester/validators/conftest.py +5 -0
  156. atdd/tester/validators/coverage_gap_report.py +321 -0
  157. atdd/tester/validators/fix_dual_ac_references.py +179 -0
  158. atdd/tester/validators/remove_duplicate_lines.py +93 -0
  159. atdd/tester/validators/test_acceptance_urn_filename_mapping.py +359 -0
  160. atdd/tester/validators/test_acceptance_urn_separator.py +166 -0
  161. atdd/tester/validators/test_artifact_naming_category.py +307 -0
  162. atdd/tester/validators/test_contract_schema_compliance.py +706 -0
  163. atdd/tester/validators/test_contracts_structure.py +200 -0
  164. atdd/tester/validators/test_coverage_adequacy.py +797 -0
  165. atdd/tester/validators/test_dual_ac_reference.py +225 -0
  166. atdd/tester/validators/test_fixture_validity.py +372 -0
  167. atdd/tester/validators/test_isolation.py +487 -0
  168. atdd/tester/validators/test_migration_coverage.py +204 -0
  169. atdd/tester/validators/test_migration_criteria.py +276 -0
  170. atdd/tester/validators/test_migration_generation.py +116 -0
  171. atdd/tester/validators/test_python_test_naming.py +410 -0
  172. atdd/tester/validators/test_red_layer_validation.py +95 -0
  173. atdd/tester/validators/test_red_python_layer_structure.py +87 -0
  174. atdd/tester/validators/test_red_supabase_layer_structure.py +90 -0
  175. atdd/tester/validators/test_telemetry_structure.py +634 -0
  176. atdd/tester/validators/test_typescript_test_naming.py +301 -0
  177. atdd/tester/validators/test_typescript_test_structure.py +84 -0
  178. atdd-0.1.0.dist-info/METADATA +191 -0
  179. atdd-0.1.0.dist-info/RECORD +183 -0
  180. atdd-0.1.0.dist-info/WHEEL +5 -0
  181. atdd-0.1.0.dist-info/entry_points.txt +2 -0
  182. atdd-0.1.0.dist-info/licenses/LICENSE +674 -0
  183. atdd-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,448 @@
1
+ """
2
+ Test contract and telemetry traceability reconciliation.
3
+
4
+ SPEC: .claude/agents/coach/utils.spec.yaml::traceability
5
+ IDs: SPEC-COACH-UTILS-0283 through SPEC-COACH-UTILS-0291
6
+
7
+ Validates:
8
+ - Detection of missing contract/telemetry references
9
+ - URN matching strategies (exact, normalized, path-based)
10
+ - Pragmatic fixes with user approval
11
+ - Bidirectional traceability validation
12
+ - Batch reconciliation reporting
13
+ - Clean 4-layer architecture
14
+
15
+ Architecture:
16
+ - Tests orchestrate command layer
17
+ - Command imports from atdd/coach/commands/traceability.py
18
+ - Leverages existing entities from atdd/planner/test_wagon_contract_traceability.py
19
+ """
20
+
21
+ import pytest
22
+ from pathlib import Path
23
+ from unittest.mock import Mock, patch, MagicMock
24
+
25
+
26
+ # ============================================================================
27
+ # Test Fixtures
28
+ # ============================================================================
29
+
30
+
31
+ @pytest.fixture
32
+ def sample_wagon_manifest():
33
+ """Sample wagon manifest with produce items."""
34
+ return {
35
+ 'wagon': 'generate-identifiers',
36
+ 'produce': [
37
+ {
38
+ 'name': 'identifiers:uuid',
39
+ 'urn': 'contract:system:identifiers:uuid',
40
+ 'to': 'internal',
41
+ 'contract': None, # Missing reference
42
+ 'telemetry': None
43
+ },
44
+ {
45
+ 'name': 'identifiers:username',
46
+ 'urn': 'contract:system:identifiers:username',
47
+ 'to': 'internal',
48
+ 'contract': None, # Missing reference
49
+ 'telemetry': None
50
+ }
51
+ ]
52
+ }
53
+
54
+
55
+ @pytest.fixture
56
+ def sample_contract_file():
57
+ """Sample contract file metadata."""
58
+ from atdd.coach.commands.traceability import ContractFile
59
+
60
+ return ContractFile(
61
+ file_path='contracts/commons/identifiers/uuid.schema.json',
62
+ contract_id='system:identifiers.uuid',
63
+ domain='system',
64
+ resource='identifiers.uuid',
65
+ version='1.0.0',
66
+ producer='wagon:generate-identifiers',
67
+ consumers=['wagon:register-account']
68
+ )
69
+
70
+
71
+ # ============================================================================
72
+ # SPEC-COACH-UTILS-0283: Detect missing contract references
73
+ # ============================================================================
74
+
75
+
76
+ @pytest.mark.platform
77
+ def test_detect_missing_contract_references(sample_wagon_manifest, sample_contract_file):
78
+ """
79
+ SPEC-COACH-UTILS-0283: Detect missing contract references in wagon manifests.
80
+
81
+ Given: Wagon manifest declares produce item with URN 'contract:system:identifiers:uuid'
82
+ And: Contract file 'contracts/commons/identifiers/uuid.schema.json' exists
83
+ And: Manifest has 'contract: null' for this produce item
84
+ When: Running traceability reconciliation
85
+ Then: Detects missing contract reference
86
+ And: Proposes fix with actual contract file path
87
+ And: Shows user the proposed change for approval
88
+ """
89
+ from atdd.coach.commands.traceability import TraceabilityReconciler
90
+
91
+ # This will fail until implementation exists
92
+ reconciler = TraceabilityReconciler()
93
+
94
+ # Parse produce items
95
+ produce_items = reconciler.parse_produce_items(sample_wagon_manifest)
96
+
97
+ # Find contracts
98
+ contracts = [sample_contract_file]
99
+
100
+ # Detect missing references
101
+ missing_refs = reconciler.detect_missing_contract_refs(produce_items, contracts)
102
+
103
+ assert len(missing_refs) > 0
104
+ assert missing_refs[0]['urn'] == 'contract:system:identifiers:uuid'
105
+ assert missing_refs[0]['proposed_fix'] == 'contracts/commons/identifiers/uuid.schema.json'
106
+
107
+
108
+ # ============================================================================
109
+ # SPEC-COACH-UTILS-0284: Reconcile contract URN variations
110
+ # ============================================================================
111
+
112
+
113
+ @pytest.mark.platform
114
+ def test_reconcile_contract_urn_variations():
115
+ """
116
+ SPEC-COACH-UTILS-0284: Reconcile contract URN with actual file using multiple matching strategies.
117
+
118
+ Given: Wagon produce URN 'contract:system:identifiers:uuid'
119
+ And: Contract file with '$id' field 'system:identifiers.uuid' (dot notation)
120
+ And: Contract file path 'contracts/commons/identifiers/uuid.schema.json'
121
+ When: Matching URN to contract file
122
+ Then: Tries exact match on $id
123
+ And: Tries normalized match (colon vs dot variations)
124
+ And: Tries path-based match
125
+ And: Returns matching contract file
126
+ """
127
+ from atdd.coach.commands.traceability import ContractMatcher
128
+
129
+ matcher = ContractMatcher()
130
+
131
+ urn = 'contract:system:identifiers:uuid'
132
+ contracts = [
133
+ {
134
+ 'file_path': 'contracts/commons/identifiers/uuid.schema.json',
135
+ 'contract_id': 'system:identifiers.uuid'
136
+ }
137
+ ]
138
+
139
+ # Should match despite dot vs colon notation
140
+ matched = matcher.find_by_urn(urn, contracts)
141
+
142
+ assert matched is not None
143
+ assert matched['file_path'] == 'contracts/commons/identifiers/uuid.schema.json'
144
+
145
+
146
+ # ============================================================================
147
+ # SPEC-COACH-UTILS-0285: Propose and apply fix
148
+ # ============================================================================
149
+
150
+
151
+ @pytest.mark.platform
152
+ def test_propose_and_apply_contract_fix(tmp_path):
153
+ """
154
+ SPEC-COACH-UTILS-0285: Propose fix for missing contract reference with user approval.
155
+
156
+ Given: Missing contract reference detected
157
+ And: Contract file path 'contracts/commons/identifiers/uuid.schema.json'
158
+ And: Wagon manifest at 'plan/generate_identifiers/_generate_identifiers.yaml'
159
+ When: User approves fix
160
+ Then: Updates wagon manifest YAML file
161
+ And: Changes 'contract: null' to 'contract: contracts/commons/identifiers/uuid.schema.json'
162
+ And: Preserves YAML formatting and structure
163
+ And: Logs change to facts/audit.log
164
+ """
165
+ from atdd.coach.commands.traceability import TraceabilityFixer
166
+
167
+ # Create temporary manifest file
168
+ manifest_file = tmp_path / "test_manifest.yaml"
169
+ manifest_content = """wagon: generate-identifiers
170
+ produce:
171
+ - name: identifiers:uuid
172
+ urn: contract:system:identifiers:uuid
173
+ to: internal
174
+ contract: null
175
+ telemetry: null
176
+ """
177
+ manifest_file.write_text(manifest_content)
178
+
179
+ fixer = TraceabilityFixer()
180
+
181
+ # Apply fix
182
+ fix_applied = fixer.apply_contract_fix(
183
+ manifest_path=str(manifest_file),
184
+ produce_name='identifiers:uuid',
185
+ contract_path='contracts/commons/identifiers/uuid.schema.json'
186
+ )
187
+
188
+ assert fix_applied is True
189
+
190
+ # Verify file was updated
191
+ updated_content = manifest_file.read_text()
192
+ assert 'contract: contracts/commons/identifiers/uuid.schema.json' in updated_content
193
+ assert 'contract: null' not in updated_content
194
+
195
+
196
+ # ============================================================================
197
+ # SPEC-COACH-UTILS-0286: Validate bidirectional traceability
198
+ # ============================================================================
199
+
200
+
201
+ @pytest.mark.platform
202
+ def test_validate_bidirectional_traceability():
203
+ """
204
+ SPEC-COACH-UTILS-0286: Validate bidirectional traceability between wagon and contract.
205
+
206
+ Given: Wagon 'generate-identifiers' declares producing 'contract:system:identifiers:uuid'
207
+ And: Contract file has 'x-artifact-metadata.producer: wagon:generate-identifiers'
208
+ When: Validating bidirectional references
209
+ Then: Confirms wagon->contract reference exists
210
+ And: Confirms contract->wagon reference matches
211
+ And: Reports as fully traced
212
+ """
213
+ from atdd.coach.commands.traceability import TraceabilityValidator
214
+
215
+ validator = TraceabilityValidator()
216
+
217
+ produce_item = {
218
+ 'wagon': 'generate-identifiers',
219
+ 'urn': 'contract:system:identifiers:uuid'
220
+ }
221
+
222
+ contract = {
223
+ 'file_path': 'contracts/commons/identifiers/uuid.schema.json',
224
+ 'producer': 'wagon:generate-identifiers'
225
+ }
226
+
227
+ is_bidirectional = validator.validate_bidirectional(produce_item, contract)
228
+
229
+ assert is_bidirectional is True
230
+
231
+
232
+ # ============================================================================
233
+ # SPEC-COACH-UTILS-0287: Detect mismatched producer
234
+ # ============================================================================
235
+
236
+
237
+ @pytest.mark.platform
238
+ def test_detect_mismatched_producer():
239
+ """
240
+ SPEC-COACH-UTILS-0287: Detect mismatched producer in contract metadata.
241
+
242
+ Given: Wagon 'generate-identifiers' declares producing 'contract:system:identifiers:uuid'
243
+ And: Contract file has 'x-artifact-metadata.producer: wagon:other-wagon'
244
+ When: Validating producer consistency
245
+ Then: Detects mismatch between wagon declaration and contract metadata
246
+ And: Reports as traceability violation
247
+ And: Proposes correction to either wagon or contract
248
+ """
249
+ from atdd.coach.commands.traceability import TraceabilityValidator
250
+
251
+ validator = TraceabilityValidator()
252
+
253
+ produce_item = {
254
+ 'wagon': 'generate-identifiers',
255
+ 'urn': 'contract:system:identifiers:uuid'
256
+ }
257
+
258
+ contract = {
259
+ 'file_path': 'contracts/commons/identifiers/uuid.schema.json',
260
+ 'producer': 'wagon:other-wagon' # Mismatch!
261
+ }
262
+
263
+ mismatch = validator.check_producer_match(produce_item, contract)
264
+
265
+ assert mismatch is not None
266
+ assert mismatch['expected'] == 'wagon:generate-identifiers'
267
+ assert mismatch['actual'] == 'wagon:other-wagon'
268
+
269
+
270
+ # ============================================================================
271
+ # SPEC-COACH-UTILS-0288: Batch reconciliation report
272
+ # ============================================================================
273
+
274
+
275
+ @pytest.mark.platform
276
+ def test_batch_reconciliation_report():
277
+ """
278
+ SPEC-COACH-UTILS-0288: Batch reconciliation report for all wagons.
279
+
280
+ Given: Multiple wagon manifests in plan/ directory
281
+ And: Multiple contract files in contracts/ directory
282
+ And: Some with missing references, some with mismatches
283
+ When: Running full repository reconciliation
284
+ Then: Scans all wagon manifests
285
+ And: Scans all contract files
286
+ And: Groups issues by wagon
287
+ And: Shows statistics (total issues, by type)
288
+ And: Provides prioritized fix list
289
+ """
290
+ from atdd.coach.commands.traceability import TraceabilityReconciler
291
+
292
+ reconciler = TraceabilityReconciler()
293
+
294
+ # This will scan the actual repo
295
+ report = reconciler.reconcile_all()
296
+
297
+ # reconcile_all() returns ReconciliationResult object
298
+ assert hasattr(report, 'total_issues')
299
+ assert hasattr(report, 'missing_contract_refs')
300
+ assert hasattr(report, 'by_wagon')
301
+ assert isinstance(report.total_issues, int)
302
+
303
+
304
+ # ============================================================================
305
+ # SPEC-COACH-UTILS-0289: Support telemetry reconciliation
306
+ # ============================================================================
307
+
308
+
309
+ @pytest.mark.platform
310
+ def test_detect_missing_telemetry_references():
311
+ """
312
+ SPEC-COACH-UTILS-0289: Support telemetry reference reconciliation.
313
+
314
+ Given: Wagon manifest declares produce item with telemetry URN
315
+ And: Telemetry file exists in telemetry/ directory
316
+ And: Manifest has 'telemetry: null' for this produce item
317
+ When: Running traceability reconciliation
318
+ Then: Detects missing telemetry reference
319
+ And: Proposes fix with actual telemetry file path
320
+ And: Applies same reconciliation logic as contracts
321
+ """
322
+ from atdd.coach.commands.traceability import TraceabilityReconciler
323
+
324
+ reconciler = TraceabilityReconciler()
325
+
326
+ wagon_manifest = {
327
+ 'wagon': 'generate-identifiers',
328
+ 'produce': [
329
+ {
330
+ 'name': 'uuid-generation-metrics',
331
+ 'urn': 'telemetry:metric:be:uuid:generation:duration',
332
+ 'to': 'internal',
333
+ 'contract': None,
334
+ 'telemetry': None # Missing reference
335
+ }
336
+ ]
337
+ }
338
+
339
+ from atdd.coach.commands.traceability import TelemetryFile
340
+
341
+ telemetry_files = [
342
+ TelemetryFile(
343
+ file_path='telemetry/metrics/be/uuid/generation_duration.yaml',
344
+ telemetry_id='telemetry:metric:be:uuid:generation:duration',
345
+ domain='metric',
346
+ resource='be.uuid.generation.duration',
347
+ producer='wagon:generate-identifiers'
348
+ )
349
+ ]
350
+
351
+ produce_items = reconciler.parse_produce_items(wagon_manifest)
352
+ missing_refs = reconciler.detect_missing_telemetry_refs(produce_items, telemetry_files)
353
+
354
+ assert len(missing_refs) > 0
355
+ assert missing_refs[0]['proposed_fix'] == 'telemetry/metrics/be/uuid/generation_duration.yaml'
356
+
357
+
358
+ # ============================================================================
359
+ # SPEC-COACH-UTILS-0290: Leverage existing test diagnostics
360
+ # ============================================================================
361
+
362
+
363
+ @pytest.mark.platform
364
+ def test_leverage_existing_test_diagnostics():
365
+ """
366
+ SPEC-COACH-UTILS-0290: Leverage existing test diagnostics from test_wagon_contract_traceability.
367
+
368
+ Given: Existing test file 'atdd/planner/test_wagon_contract_traceability.py'
369
+ And: Test provides detailed reconciliation entities and use cases
370
+ When: Running traceability command
371
+ Then: Imports and uses WagonManifest, ContractFile, ProduceItem entities
372
+ And: Imports and uses ManifestParser, ContractFinder use cases
373
+ And: Avoids code duplication
374
+ And: Reuses proven diagnostic logic
375
+ """
376
+ # Verify we can import from existing test file
377
+ # This demonstrates code reuse
378
+ try:
379
+ # The implementation should import from this module
380
+ from atdd.coach.commands.traceability import TraceabilityReconciler
381
+
382
+ # Verify the reconciler uses shared entities
383
+ reconciler = TraceabilityReconciler()
384
+
385
+ # Check that it has methods that align with existing diagnostic logic
386
+ assert hasattr(reconciler, 'parse_produce_items')
387
+ assert hasattr(reconciler, 'detect_missing_contract_refs')
388
+
389
+ except ImportError:
390
+ pytest.fail("Implementation should import from test diagnostic module")
391
+
392
+
393
+ # ============================================================================
394
+ # SPEC-COACH-UTILS-0291: Clean 4-layer architecture
395
+ # ============================================================================
396
+
397
+
398
+ @pytest.mark.platform
399
+ def test_clean_architecture_layers():
400
+ """
401
+ SPEC-COACH-UTILS-0291: Clean 4-layer architecture with entities, use cases, adapters, commands.
402
+
403
+ Given: Traceability command implementation
404
+ When: Refactoring for clean architecture
405
+ Then: Layer 1 (Entities) - ProduceItem, ContractFile, ReconciliationResult
406
+ And: Layer 2 (Use Cases) - ManifestParser, ContractFinder, TraceabilityReconciler
407
+ And: Layer 3 (Adapters) - ReportFormatter, YAMLUpdater
408
+ And: Layer 4 (Command) - CLI entry point, user interaction, orchestration
409
+ And: Each layer depends only on inner layers
410
+ And: Business logic isolated from I/O
411
+ """
412
+ from atdd.coach.commands.traceability import (
413
+ # Layer 1: Entities
414
+ ProduceItem,
415
+ ContractFile,
416
+ ReconciliationResult,
417
+
418
+ # Layer 2: Use Cases
419
+ ManifestParser,
420
+ ContractFinder,
421
+ TraceabilityReconciler,
422
+
423
+ # Layer 3: Adapters
424
+ ReportFormatter,
425
+ YAMLUpdater
426
+ )
427
+
428
+ # Verify entities are dataclasses (immutable domain models)
429
+ assert hasattr(ProduceItem, '__dataclass_fields__')
430
+ assert hasattr(ContractFile, '__dataclass_fields__')
431
+ assert hasattr(ReconciliationResult, '__dataclass_fields__')
432
+
433
+ # Verify use cases have business logic methods
434
+ parser = ManifestParser()
435
+ assert callable(getattr(parser, 'parse_manifest', None))
436
+
437
+ finder = ContractFinder()
438
+ assert callable(getattr(finder, 'find_by_urn', None))
439
+
440
+ reconciler = TraceabilityReconciler()
441
+ assert callable(getattr(reconciler, 'reconcile_all', None))
442
+
443
+ # Verify adapters have presentation/I/O methods
444
+ formatter = ReportFormatter()
445
+ assert callable(getattr(formatter, 'format_report', None))
446
+
447
+ updater = YAMLUpdater()
448
+ assert callable(getattr(updater, 'update_yaml_field', None))
@@ -0,0 +1,108 @@
1
+ """
2
+ SPEC-COACH-UTILS-0291: Add implementation paths array to feature manifest files
3
+
4
+ Tests updating feature manifests with implementation paths from filesystem.
5
+ """
6
+ import pytest
7
+ import yaml
8
+ from pathlib import Path
9
+
10
+
11
+ def test_update_feature_implementation_paths(tmp_path):
12
+ """
13
+ SPEC-COACH-UTILS-0291: Add implementation paths array to feature manifest files
14
+
15
+ Given: Feature manifests exist at plan/{wagon_snake}/features/{feature_snake}.yaml
16
+ Features have URNs in format feature:wagon-slug.feature-slug
17
+ Implementation directories may exist in python/, lib/, supabase/functions/, packages/
18
+ Filesystem uses snake_case for directory names
19
+ When: Updating feature manifests with implementation paths
20
+ Then: Each feature manifest gets a paths array field
21
+ paths array contains only existing implementation directories
22
+ Path format is language_dir/wagon_snake/feature_snake/
23
+ URN kebab-case is converted to filesystem snake_case
24
+ Checks these locations python/, lib/, supabase/functions/, packages/
25
+ Empty array if no implementations exist
26
+ Feature manifests are updated in place
27
+ YAML structure and formatting preserved
28
+ """
29
+ # Setup directory structure
30
+ plan_dir = tmp_path / "plan"
31
+ plan_dir.mkdir()
32
+
33
+ # Create wagon directory with features subdirectory
34
+ wagon_dir = plan_dir / "test_wagon"
35
+ wagon_dir.mkdir()
36
+ features_dir = wagon_dir / "features"
37
+ features_dir.mkdir()
38
+
39
+ # Create feature manifest
40
+ feature_manifest = features_dir / "test_feature.yaml"
41
+ feature_data = {
42
+ "feature": "test-feature",
43
+ "urn": "feature:test-wagon.test-feature",
44
+ "description": "Test feature for path updates",
45
+ "acceptance": []
46
+ }
47
+ with open(feature_manifest, 'w') as f:
48
+ yaml.dump(feature_data, f, default_flow_style=False, sort_keys=False)
49
+
50
+ # Create another feature manifest with no implementations
51
+ feature_no_impl = features_dir / "no_impl_feature.yaml"
52
+ feature_no_impl_data = {
53
+ "feature": "no-impl-feature",
54
+ "urn": "feature:test-wagon.no-impl-feature",
55
+ "description": "Feature with no implementations",
56
+ "acceptance": []
57
+ }
58
+ with open(feature_no_impl, 'w') as f:
59
+ yaml.dump(feature_no_impl_data, f, default_flow_style=False, sort_keys=False)
60
+
61
+ # Create some implementation directories (snake_case)
62
+ # Python implementation exists
63
+ python_dir = tmp_path / "python" / "test_wagon" / "test_feature"
64
+ python_dir.mkdir(parents=True)
65
+
66
+ # Dart lib implementation exists
67
+ lib_dir = tmp_path / "lib" / "test_wagon" / "test_feature"
68
+ lib_dir.mkdir(parents=True)
69
+
70
+ # Supabase functions implementation DOES NOT exist (intentionally)
71
+ # packages implementation DOES NOT exist (intentionally)
72
+
73
+ # Import and call the update function
74
+ from atdd.coach.commands.registry import RegistryBuilder
75
+
76
+ builder = RegistryBuilder(tmp_path)
77
+ builder.update_feature_implementation_paths()
78
+
79
+ # Load the updated feature manifest
80
+ with open(feature_manifest, 'r') as f:
81
+ updated_data = yaml.safe_load(f)
82
+
83
+ # Assertions for test_feature
84
+ assert "paths" in updated_data, "Feature manifest should have paths field"
85
+ assert isinstance(updated_data["paths"], list), "paths should be an array"
86
+ assert len(updated_data["paths"]) == 2, "Should have 2 implementation paths (python and lib)"
87
+
88
+ expected_python_path = "python/test_wagon/test_feature/"
89
+ expected_lib_path = "lib/test_wagon/test_feature/"
90
+
91
+ assert expected_python_path in updated_data["paths"], f"Should contain {expected_python_path}"
92
+ assert expected_lib_path in updated_data["paths"], f"Should contain {expected_lib_path}"
93
+
94
+ # Should NOT contain paths that don't exist
95
+ assert "supabase/functions/test_wagon/test_feature/" not in updated_data["paths"]
96
+ assert "packages/test_wagon/test_feature/" not in updated_data["paths"]
97
+
98
+ # Other fields should remain unchanged
99
+ assert updated_data["feature"] == "test-feature"
100
+ assert updated_data["urn"] == "feature:test-wagon.test-feature"
101
+ assert updated_data["description"] == "Test feature for path updates"
102
+
103
+ # Check feature with no implementations
104
+ with open(feature_no_impl, 'r') as f:
105
+ no_impl_data = yaml.safe_load(f)
106
+
107
+ assert "paths" in no_impl_data, "Feature with no impls should still have paths field"
108
+ assert no_impl_data["paths"] == [], "Should have empty paths array when no implementations exist"