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,359 @@
1
+ """
2
+ Platform validation: Test filename generation from acceptance URNs.
3
+
4
+ Validates that actual test files in the repository follow the URN-based
5
+ naming convention for all supported languages.
6
+
7
+ Spec: SPEC-TESTER-CONV-0069 through SPEC-TESTER-CONV-0078
8
+ URN: acc:coach:platform-validation.filename-mapping
9
+ """
10
+
11
+ import os
12
+ import re
13
+ from pathlib import Path
14
+ import pytest
15
+
16
+
17
+ REPO_ROOT = Path(__file__).parent.parent.parent
18
+
19
+
20
+ # SPEC-TESTER-CONV-0069: Dart URN to filename mapping
21
+ def test_dart_urn_to_filename():
22
+ """Test Dart filename generation from URN."""
23
+ from atdd.tester.utils.filename import dart_filename
24
+
25
+ test_cases = [
26
+ ("acc:maintain-ux:C004-E2E-019-user-connection", "C004_E2E_019_user_connection_test.dart"),
27
+ ("acc:maintain-ux:C004-UNIT-001", "C004_UNIT_001_test.dart"),
28
+ ("acc:stage-characters:K005-WIDGET-012-avatar-display", "K005_WIDGET_012_avatar_display_test.dart"),
29
+ ]
30
+
31
+ for urn, expected in test_cases:
32
+ assert dart_filename(urn) == expected
33
+
34
+
35
+ # SPEC-TESTER-CONV-0070: TypeScript URN to filename mapping
36
+ def test_typescript_urn_to_filename():
37
+ """Test TypeScript filename generation from URN."""
38
+ from atdd.tester.utils.filename import typescript_filename
39
+
40
+ test_cases = [
41
+ ("acc:maintain-ux:C004-E2E-019-user-connection", "c004-e2e-019-user-connection.test.ts"),
42
+ ("acc:maintain-ux:C004-HTTP-001", "c004-http-001.test.ts"),
43
+ ("acc:resolve-dilemmas:R007-EVENT-005-choice-made", "r007-event-005-choice-made.test.ts"),
44
+ ]
45
+
46
+ for urn, expected in test_cases:
47
+ assert typescript_filename(urn) == expected
48
+
49
+
50
+ # SPEC-TESTER-CONV-0071: Python URN to filename mapping
51
+ def test_python_urn_to_filename():
52
+ """Test Python filename generation from URN."""
53
+ from atdd.tester.utils.filename import python_filename
54
+
55
+ test_cases = [
56
+ ("acc:maintain-ux:C004-E2E-019-user-connection", "test_c004_e2e_019_user_connection.py"),
57
+ ("acc:commit-state:D001-UNIT-042", "test_d001_unit_042.py"),
58
+ ("acc:construct-graph:M008-INTEGRATION-007-graph-build", "test_m008_integration_007_graph_build.py"),
59
+ ]
60
+
61
+ for urn, expected in test_cases:
62
+ assert python_filename(urn) == expected
63
+
64
+
65
+ # SPEC-TESTER-CONV-0072: Go URN to filename mapping
66
+ def test_go_urn_to_filename():
67
+ """Test Go filename generation from URN."""
68
+ from atdd.tester.utils.filename import go_filename
69
+
70
+ test_cases = [
71
+ ("acc:maintain-ux:C004-E2E-019-user-connection", "c004_e2e_019_user_connection_test.go"),
72
+ ("acc:commit-state:D001-UNIT-042", "d001_unit_042_test.go"),
73
+ ]
74
+
75
+ for urn, expected in test_cases:
76
+ assert go_filename(urn) == expected
77
+
78
+
79
+ # SPEC-TESTER-CONV-0073: Java/Kotlin URN to classname mapping
80
+ def test_java_urn_to_classname():
81
+ """Test Java/Kotlin classname generation from URN."""
82
+ from atdd.tester.utils.filename import java_classname
83
+
84
+ test_cases = [
85
+ ("acc:maintain-ux:C004-E2E-019-user-connection", "C004E2E019UserConnectionTest"),
86
+ ("acc:commit-state:D001-UNIT-042", "D001UNIT042Test"),
87
+ ("acc:stage-characters:K005-INTEGRATION-003-full-flow", "K005INTEGRATION003FullFlowTest"),
88
+ ]
89
+
90
+ for urn, expected in test_cases:
91
+ assert java_classname(urn) == expected
92
+
93
+
94
+ # SPEC-TESTER-CONV-0074: Handle URNs without slug
95
+ def test_no_slug_handling():
96
+ """Test filename generation for URNs without optional slug."""
97
+ from atdd.tester.utils.filename import dart_filename, typescript_filename, python_filename
98
+
99
+ urn = "acc:maintain-ux:C004-E2E-019"
100
+
101
+ assert dart_filename(urn) == "C004_E2E_019_test.dart"
102
+ assert typescript_filename(urn) == "c004-e2e-019.test.ts"
103
+ assert python_filename(urn) == "test_c004_e2e_019.py"
104
+
105
+
106
+ def test_typescript_preact_urn_to_filename():
107
+ """Test Preact TypeScript filename generation from URN."""
108
+ from atdd.tester.utils.filename import typescript_preact_filename
109
+
110
+ test_cases = [
111
+ ("acc:maintain-ux:C004-E2E-019-user-connection", "C004_E2E_019_user_connection.test.ts"),
112
+ ("acc:maintain-ux:C004-HTTP-001", "C004_HTTP_001.test.ts"),
113
+ ("acc:maintain-ux:C001-WIDGET-001-button", "C001_WIDGET_001_button.test.tsx"),
114
+ ]
115
+
116
+ for urn, expected in test_cases:
117
+ assert typescript_preact_filename(urn) == expected
118
+
119
+
120
+ # SPEC-TESTER-CONV-0076: URN pattern validation
121
+ def test_urn_pattern_validation():
122
+ """Test URN regex pattern matches valid acceptance URNs."""
123
+ from atdd.tester.utils.filename import URN_PATTERN
124
+
125
+ valid_urns = [
126
+ "acc:maintain-ux:C004-E2E-019-user-connection",
127
+ "acc:pace-dilemmas:P003-UNIT-042",
128
+ "acc:predict-cascade:L009-HTTP-001-api-endpoint",
129
+ "acc:commit-state:D001-EVENT-005",
130
+ ]
131
+
132
+ for urn in valid_urns:
133
+ assert re.match(URN_PATTERN, urn), f"Should match: {urn}"
134
+
135
+ invalid_urns = [
136
+ "acc:maintain_ux:C004-E2E-019", # Underscore not allowed
137
+ "wmbt:maintain-ux:C004-E2E-019", # Wrong prefix
138
+ "acc:maintain-ux:C4-E2E-019", # WMBT not zero-padded
139
+ ]
140
+
141
+ for urn in invalid_urns:
142
+ assert not re.match(URN_PATTERN, urn), f"Should not match: {urn}"
143
+
144
+
145
+ # SPEC-TESTER-CONV-0078: Platform validation enforces filename convention compliance
146
+ def test_dart_files_match_convention():
147
+ """Validate Dart test files follow URN-based naming convention."""
148
+ from atdd.tester.utils.filename import parse_acceptance_urn, dart_filename
149
+
150
+ # Pattern: {WMBT}_{HARNESS}_{NNN}[_{slug}]_test.dart
151
+ dart_pattern = re.compile(r'^([A-Z][0-9]{3})_([A-Z0-9]+)_([0-9]{3})(?:_([a-z0-9_]+))?_test\.dart$')
152
+
153
+ test_dirs = [
154
+ REPO_ROOT / "test",
155
+ REPO_ROOT / "integration_test",
156
+ ]
157
+
158
+ violations = []
159
+
160
+ for test_dir in test_dirs:
161
+ if not test_dir.exists():
162
+ continue
163
+
164
+ for dart_file in test_dir.rglob("*_test.dart"):
165
+ filename = dart_file.name
166
+
167
+ # Check pattern compliance
168
+ match = dart_pattern.match(filename)
169
+ if not match:
170
+ # Check if there's a URN comment to determine expected filename
171
+ content = dart_file.read_text()
172
+ urn_match = re.search(r'// urn: (acc:[a-z][a-z0-9-]*:[A-Z][0-9]{3}-[A-Z0-9]+-[0-9]{3}(?:-[a-z0-9-]+)?)', content)
173
+
174
+ if urn_match:
175
+ urn = urn_match.group(1)
176
+ expected = dart_filename(urn)
177
+ violations.append({
178
+ 'file': str(dart_file.relative_to(REPO_ROOT)),
179
+ 'actual': filename,
180
+ 'expected': expected,
181
+ 'urn': urn
182
+ })
183
+
184
+ # Allow some violations during migration, but report them
185
+ if violations:
186
+ report = "\n".join([
187
+ f" {v['file']}: {v['actual']} → {v['expected']} (URN: {v['urn']})"
188
+ for v in violations[:5] # Show first 5
189
+ ])
190
+ print(f"\nFilename convention violations found:\n{report}")
191
+
192
+ # For now, just log violations without failing
193
+ # In the future, this should assert len(violations) == 0
194
+ assert True, "Validation complete (violations logged)"
195
+
196
+
197
+ def test_typescript_files_match_convention():
198
+ """Validate TypeScript test files follow URN-based naming convention."""
199
+ from atdd.tester.utils.filename import typescript_filename
200
+
201
+ # Pattern: {wmbt}-{harness}-{nnn}[-{slug}].test.ts
202
+ ts_pattern = re.compile(r'^([a-z][0-9]{3})-([a-z0-9]+)-([0-9]{3})(?:-([a-z0-9-]+))?\.test\.ts$')
203
+
204
+ test_dirs = [
205
+ REPO_ROOT / "supabase" / "functions",
206
+ REPO_ROOT / "e2e",
207
+ ]
208
+
209
+ violations = []
210
+
211
+ for test_dir in test_dirs:
212
+ if not test_dir.exists():
213
+ continue
214
+
215
+ for ts_file in test_dir.rglob("*.test.ts"):
216
+ filename = ts_file.name
217
+ match = ts_pattern.match(filename)
218
+
219
+ if not match:
220
+ content = ts_file.read_text()
221
+ urn_match = re.search(r'// urn: (acc:[a-z][a-z0-9-]*:[A-Z][0-9]{3}-[A-Z0-9]+-[0-9]{3}(?:-[a-z0-9-]+)?)', content)
222
+
223
+ if urn_match:
224
+ urn = urn_match.group(1)
225
+ expected = typescript_filename(urn)
226
+ violations.append({
227
+ 'file': str(ts_file.relative_to(REPO_ROOT)),
228
+ 'actual': filename,
229
+ 'expected': expected,
230
+ 'urn': urn
231
+ })
232
+
233
+ if violations:
234
+ report = "\n".join([
235
+ f" {v['file']}: {v['actual']} → {v['expected']} (URN: {v['urn']})"
236
+ for v in violations[:5]
237
+ ])
238
+ print(f"\nFilename convention violations found:\n{report}")
239
+
240
+ assert True, "Validation complete (violations logged)"
241
+
242
+
243
+ def test_typescript_preact_files_match_convention():
244
+ """Validate Preact TypeScript test files follow URN-based naming convention."""
245
+ from atdd.tester.utils.filename import typescript_preact_filename
246
+
247
+ ts_pattern = re.compile(r'^([A-Z][0-9]{3})_([A-Z0-9]+)_([0-9]{3})(?:_([a-z0-9_]+))?\.test\.ts(x)?$')
248
+
249
+ test_dirs = [
250
+ REPO_ROOT / "web" / "tests",
251
+ ]
252
+
253
+ violations = []
254
+
255
+ for test_dir in test_dirs:
256
+ if not test_dir.exists():
257
+ continue
258
+
259
+ ts_files = list(test_dir.rglob("*.test.ts")) + list(test_dir.rglob("*.test.tsx"))
260
+ for ts_file in ts_files:
261
+ filename = ts_file.name
262
+ match = ts_pattern.match(filename)
263
+
264
+ if not match:
265
+ content = ts_file.read_text()
266
+ urn_match = re.search(
267
+ r'//\s*(?:urn|URN):\s*(acc:[a-z][a-z0-9-]*:[A-Z][0-9]{3}-[A-Z0-9]+-[0-9]{3}(?:-[a-z0-9-]+)?)',
268
+ content
269
+ )
270
+
271
+ if urn_match:
272
+ urn = urn_match.group(1)
273
+ expected = typescript_preact_filename(urn)
274
+ violations.append({
275
+ 'file': str(ts_file.relative_to(REPO_ROOT)),
276
+ 'actual': filename,
277
+ 'expected': expected,
278
+ 'urn': urn
279
+ })
280
+
281
+ if violations:
282
+ report = "\n".join([
283
+ f" {v['file']}: {v['actual']} → {v['expected']} (URN: {v['urn']})"
284
+ for v in violations[:5]
285
+ ])
286
+ print(f"\nFilename convention violations found:\n{report}")
287
+
288
+ assert True, "Validation complete (violations logged)"
289
+
290
+
291
+ def test_python_files_match_convention():
292
+ """Validate Python test files follow URN-based naming convention."""
293
+ from atdd.tester.utils.filename import python_filename
294
+
295
+ # Pattern: test_{wmbt}_{harness}_{nnn}[_{slug}].py
296
+ py_pattern = re.compile(r'^test_([a-z][0-9]{3})_([a-z0-9]+)_([0-9]{3})(?:_([a-z0-9_]+))?\.py$')
297
+
298
+ test_dirs = [
299
+ REPO_ROOT / "tests",
300
+ REPO_ROOT / "atdd" / "coach" / "validators",
301
+ REPO_ROOT / "atdd" / "tester" / "validators",
302
+ ]
303
+
304
+ violations = []
305
+
306
+ for test_dir in test_dirs:
307
+ if not test_dir.exists():
308
+ continue
309
+
310
+ for py_file in test_dir.rglob("test_*.py"):
311
+ filename = py_file.name
312
+ match = py_pattern.match(filename)
313
+
314
+ if not match:
315
+ try:
316
+ content = py_file.read_text()
317
+ urn_match = re.search(r'# urn: (acc:[a-z][a-z0-9-]*:[A-Z][0-9]{3}-[A-Z0-9]+-[0-9]{3}(?:-[a-z0-9-]+)?)', content)
318
+
319
+ if urn_match:
320
+ urn = urn_match.group(1)
321
+ expected = python_filename(urn)
322
+ violations.append({
323
+ 'file': str(py_file.relative_to(REPO_ROOT)),
324
+ 'actual': filename,
325
+ 'expected': expected,
326
+ 'urn': urn
327
+ })
328
+ except Exception:
329
+ pass # Skip files that can't be read
330
+
331
+ if violations:
332
+ report = "\n".join([
333
+ f" {v['file']}: {v['actual']} → {v['expected']} (URN: {v['urn']})"
334
+ for v in violations[:5]
335
+ ])
336
+ print(f"\nFilename convention violations found:\n{report}")
337
+
338
+ assert True, "Validation complete (violations logged)"
339
+
340
+
341
+ def test_urn_comment_extraction():
342
+ """Test extraction of URN from test file comments."""
343
+ # Dart style: // urn: acc:...
344
+ dart_content = """
345
+ // urn: acc:maintain-ux:C004-E2E-019-user-connection
346
+ import 'package:flutter_test/flutter_test.dart';
347
+ """
348
+ urn_match = re.search(r'// urn: (acc:[^\s]+)', dart_content)
349
+ assert urn_match
350
+ assert urn_match.group(1) == "acc:maintain-ux:C004-E2E-019-user-connection"
351
+
352
+ # Python style: # urn: acc:...
353
+ python_content = """
354
+ # urn: acc:commit-state:D001-UNIT-042
355
+ import pytest
356
+ """
357
+ urn_match = re.search(r'# urn: (acc:[^\s]+)', python_content)
358
+ assert urn_match
359
+ assert urn_match.group(1) == "acc:commit-state:D001-UNIT-042"
@@ -0,0 +1,166 @@
1
+ """
2
+ Platform validation for Acceptance URN format (Updated for SPEC-COACH-UTILS-0282).
3
+
4
+ SPEC-COACH-UTILS-0282: Acceptance URN refactored to include WMBT ID and harness
5
+ SPEC-PLANNER-CONV-0052: Acceptance URN separator follows hierarchy-facet rule
6
+ SPEC-PLANNER-CONV-0057: URNBuilder.acceptance updated for new separator pattern
7
+ SPEC-PLANNER-CONV-0058: Acceptance schema updated to reflect new URN pattern
8
+ SPEC-TESTER-CONV-0068 through SPEC-TESTER-CONV-0078: Test filename generation from URNs
9
+
10
+ Background:
11
+ - URN rule: colon = hierarchy, dash = facet grouping
12
+ - New Acceptance URN structure: acc:{wagon}:{wmbt_id}-{harness}-{NNN}[-{slug}]
13
+ - Wagon is hierarchical (use colon)
14
+ - wmbt_id, harness, sequence, and optional slug are facets (use dash)
15
+
16
+ Filename Generation:
17
+ - URN-based filename generation implemented in atdd.tester.utils.filename
18
+ - Convention documented in .claude/conventions/tester/filename.convention.yaml
19
+ - See test_acceptance_urn_filename_mapping.py for filename validation tests
20
+ """
21
+
22
+ import pytest
23
+ import yaml
24
+ import re
25
+ from pathlib import Path
26
+
27
+ # Path constants
28
+ REPO_ROOT = Path(__file__).resolve().parents[4]
29
+ CONVENTIONS_DIR = REPO_ROOT / "atdd" / "planner" / "conventions"
30
+ SCHEMAS_DIR = REPO_ROOT / "atdd" / "planner" / "schemas"
31
+
32
+
33
+ # SPEC-COACH-UTILS-0282
34
+ def test_acceptance_urn_format_updated():
35
+ """
36
+ Given: Current format in acceptance.convention.yaml
37
+ When: Applying new format with WMBT ID and harness
38
+ Then: URN examples follow pattern acc:{wagon}:{wmbt_id}-{harness}-{NNN}[-{slug}]
39
+ Example: 'acc:maintain-ux:C004-E2E-019-user-connection'
40
+ """
41
+ acceptance_convention_path = CONVENTIONS_DIR / "acceptance.convention.yaml"
42
+
43
+ with open(acceptance_convention_path, 'r') as f:
44
+ convention = yaml.safe_load(f)
45
+
46
+ # Check urn_generation section for example
47
+ urn_generation = convention.get('urn_generation', {})
48
+
49
+ # Get pattern
50
+ pattern = urn_generation.get('pattern', '')
51
+ assert pattern == "acc:{wagon}:{wmbt_id}-{harness}-{NNN}[-{slug}]", \
52
+ f"Expected new pattern format, got: {pattern}"
53
+
54
+ # Get example (multi-line string with code)
55
+ example_code = urn_generation.get('example', '')
56
+ assert len(example_code) > 0, "Should have example code"
57
+
58
+ # Extract URN examples from comments in the code
59
+ # Look for patterns like: # Returns: "acc:authenticate-user:C004-E2E-019"
60
+ urn_matches = re.findall(r'acc:[a-z][a-z0-9-]*:[DLPCEMYRK]\d{3}-[A-Z0-9]+-\d{3}(?:-[a-z0-9-]+)?', example_code)
61
+ assert len(urn_matches) > 0, f"Should have at least one URN example in code, got: {example_code}"
62
+
63
+ # Verify at least one URN matches new format
64
+ for urn in urn_matches:
65
+ # Verify structure
66
+ parts = urn.split(':')
67
+ assert len(parts) == 3, f"URN should have 3 colon-separated parts, got: {urn}"
68
+ assert parts[0] == 'acc', f"URN kind should be 'acc', got: {parts[0]}"
69
+
70
+ # Parts[2] should have format {wmbt_id}-{harness}-{seq}[-{slug}]
71
+ facets = parts[2]
72
+ assert '-' in facets, f"WMBT facets should be dash-separated, got: {facets}"
73
+
74
+ facet_parts = facets.split('-')
75
+ assert len(facet_parts) >= 3, f"Should have at least wmbt_id, harness, seq, got: {facets}"
76
+
77
+ # Check WMBT ID format: [DLPCEMYRK]\d{3}
78
+ wmbt_id = facet_parts[0]
79
+ assert re.match(r'^[DLPCEMYRK]\d{3}$', wmbt_id), \
80
+ f"WMBT ID should match step-coded format, got: {wmbt_id}"
81
+
82
+ # At least one valid URN found
83
+ break
84
+
85
+
86
+ # SPEC-COACH-UTILS-0282
87
+ def test_urn_builder_acceptance_separator(monkeypatch):
88
+ """
89
+ Given: URNBuilder utility exists
90
+ When: Using new format with WMBT ID and harness
91
+ Then: acceptance() method generates: acc:{wagon}:{wmbt_id}-{harness}-{NNN}[-{slug}]
92
+ """
93
+ # Try to import URNBuilder from current location
94
+ try:
95
+ from atdd.coach.utils.graph.urn import URNBuilder
96
+
97
+ # Test URNBuilder.acceptance method with new signature
98
+ # New signature: acceptance(wagon_id, wmbt_id, harness_code, seq, slug=None)
99
+ urn = URNBuilder.acceptance("authenticate-user", "E005", "UNIT", "201")
100
+
101
+ assert urn == "acc:authenticate-user:E005-UNIT-201", \
102
+ f"Expected 'acc:authenticate-user:E005-UNIT-201', got '{urn}'"
103
+
104
+ # Verify structure
105
+ assert urn.count(':') == 2, \
106
+ f"URN should have 2 colons (hierarchy separators), got: {urn}"
107
+ assert urn.count('-') >= 2, \
108
+ f"URN should have at least 2 dashes (facet separators), got: {urn}"
109
+
110
+ # Test with optional slug
111
+ urn_with_slug = URNBuilder.acceptance("maintain-ux", "C004", "E2E", "019", "user-connection")
112
+ assert urn_with_slug == "acc:maintain-ux:C004-E2E-019-user-connection", \
113
+ f"Expected 'acc:maintain-ux:C004-E2E-019-user-connection', got '{urn_with_slug}'"
114
+
115
+ except ImportError as e:
116
+ pytest.skip(f"URNBuilder not yet implemented or location changed: {e}")
117
+
118
+
119
+ # SPEC-PLANNER-CONV-0058
120
+ def test_schema_validates_new_urn_format():
121
+ """
122
+ Given: Acceptance schema validates URN format
123
+ When: Updating schema to match new convention
124
+ Then: URN pattern updated to match acc:{wagon}:{nnn}.{acceptance_id}
125
+ Schema validation accepts new format
126
+ """
127
+ # Find acceptance schema
128
+ acceptance_schema_files = list(SCHEMAS_DIR.glob("acceptance*.json"))
129
+
130
+ if not acceptance_schema_files:
131
+ pytest.skip("No acceptance schema file found")
132
+
133
+ import json
134
+ schema_path = acceptance_schema_files[0]
135
+
136
+ with open(schema_path, 'r') as f:
137
+ schema = json.load(f)
138
+
139
+ # Look for URN pattern in schema
140
+ # This could be in various locations, so we'll search recursively
141
+ def find_urn_pattern(obj, path=""):
142
+ """Recursively search for URN pattern or examples"""
143
+ if isinstance(obj, dict):
144
+ # Check if this is a URN field
145
+ if 'pattern' in obj and path.endswith('urn'):
146
+ pattern = obj['pattern']
147
+ # Pattern should allow colon between wagon and sequence
148
+ # Pattern example: ^acc:[a-z-]+:\d{3}\.[A-Z-]+$
149
+ if 'acc:' in pattern:
150
+ # Verify pattern uses colon separator
151
+ # Should NOT have pattern like acc:[a-z-]+\.\d{3}
152
+ assert r'\.' not in pattern.split(':')[1] if len(pattern.split(':')) > 1 else True, \
153
+ f"Pattern should use colon between wagon and sequence, got: {pattern}"
154
+
155
+ for key, value in obj.items():
156
+ find_urn_pattern(value, f"{path}.{key}")
157
+
158
+ elif isinstance(obj, list):
159
+ for item in obj:
160
+ find_urn_pattern(item, path)
161
+
162
+ find_urn_pattern(schema)
163
+
164
+ # Test would pass if schema is updated correctly
165
+ # For now, we're just checking structure exists
166
+ assert True, "Schema structure validated"