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.
Files changed (184) hide show
  1. atdd/__init__.py +6 -0
  2. atdd/__main__.py +4 -0
  3. atdd/cli.py +404 -0
  4. atdd/coach/__init__.py +0 -0
  5. atdd/coach/commands/__init__.py +0 -0
  6. atdd/coach/commands/add_persistence_metadata.py +215 -0
  7. atdd/coach/commands/analyze_migrations.py +188 -0
  8. atdd/coach/commands/consumers.py +720 -0
  9. atdd/coach/commands/infer_governance_status.py +149 -0
  10. atdd/coach/commands/initializer.py +177 -0
  11. atdd/coach/commands/interface.py +1078 -0
  12. atdd/coach/commands/inventory.py +565 -0
  13. atdd/coach/commands/migration.py +240 -0
  14. atdd/coach/commands/registry.py +1560 -0
  15. atdd/coach/commands/session.py +430 -0
  16. atdd/coach/commands/sync.py +405 -0
  17. atdd/coach/commands/test_interface.py +399 -0
  18. atdd/coach/commands/test_runner.py +141 -0
  19. atdd/coach/commands/tests/__init__.py +1 -0
  20. atdd/coach/commands/tests/test_telemetry_array_validation.py +235 -0
  21. atdd/coach/commands/traceability.py +4264 -0
  22. atdd/coach/conventions/session.convention.yaml +754 -0
  23. atdd/coach/overlays/__init__.py +2 -0
  24. atdd/coach/overlays/claude.md +2 -0
  25. atdd/coach/schemas/config.schema.json +34 -0
  26. atdd/coach/schemas/manifest.schema.json +101 -0
  27. atdd/coach/templates/ATDD.md +282 -0
  28. atdd/coach/templates/SESSION-TEMPLATE.md +327 -0
  29. atdd/coach/utils/__init__.py +0 -0
  30. atdd/coach/utils/graph/__init__.py +0 -0
  31. atdd/coach/utils/graph/urn.py +875 -0
  32. atdd/coach/validators/__init__.py +0 -0
  33. atdd/coach/validators/shared_fixtures.py +365 -0
  34. atdd/coach/validators/test_enrich_wagon_registry.py +167 -0
  35. atdd/coach/validators/test_registry.py +575 -0
  36. atdd/coach/validators/test_session_validation.py +1183 -0
  37. atdd/coach/validators/test_traceability.py +448 -0
  38. atdd/coach/validators/test_update_feature_paths.py +108 -0
  39. atdd/coach/validators/test_validate_contract_consumers.py +297 -0
  40. atdd/coder/__init__.py +1 -0
  41. atdd/coder/conventions/adapter.recipe.yaml +88 -0
  42. atdd/coder/conventions/backend.convention.yaml +460 -0
  43. atdd/coder/conventions/boundaries.convention.yaml +666 -0
  44. atdd/coder/conventions/commons.convention.yaml +460 -0
  45. atdd/coder/conventions/complexity.recipe.yaml +109 -0
  46. atdd/coder/conventions/component-naming.convention.yaml +178 -0
  47. atdd/coder/conventions/design.convention.yaml +327 -0
  48. atdd/coder/conventions/design.recipe.yaml +273 -0
  49. atdd/coder/conventions/dto.convention.yaml +660 -0
  50. atdd/coder/conventions/frontend.convention.yaml +542 -0
  51. atdd/coder/conventions/green.convention.yaml +1012 -0
  52. atdd/coder/conventions/presentation.convention.yaml +587 -0
  53. atdd/coder/conventions/refactor.convention.yaml +535 -0
  54. atdd/coder/conventions/technology.convention.yaml +206 -0
  55. atdd/coder/conventions/tests/__init__.py +0 -0
  56. atdd/coder/conventions/tests/test_adapter_recipe.py +302 -0
  57. atdd/coder/conventions/tests/test_complexity_recipe.py +289 -0
  58. atdd/coder/conventions/tests/test_component_taxonomy.py +278 -0
  59. atdd/coder/conventions/tests/test_component_urn_naming.py +165 -0
  60. atdd/coder/conventions/tests/test_thinness_recipe.py +286 -0
  61. atdd/coder/conventions/thinness.recipe.yaml +82 -0
  62. atdd/coder/conventions/train.convention.yaml +325 -0
  63. atdd/coder/conventions/verification.protocol.yaml +53 -0
  64. atdd/coder/schemas/design_system.schema.json +361 -0
  65. atdd/coder/validators/__init__.py +0 -0
  66. atdd/coder/validators/test_commons_structure.py +485 -0
  67. atdd/coder/validators/test_complexity.py +416 -0
  68. atdd/coder/validators/test_cross_language_consistency.py +431 -0
  69. atdd/coder/validators/test_design_system_compliance.py +413 -0
  70. atdd/coder/validators/test_dto_testing_patterns.py +268 -0
  71. atdd/coder/validators/test_green_cross_stack_layers.py +168 -0
  72. atdd/coder/validators/test_green_layer_dependencies.py +148 -0
  73. atdd/coder/validators/test_green_python_layer_structure.py +103 -0
  74. atdd/coder/validators/test_green_supabase_layer_structure.py +103 -0
  75. atdd/coder/validators/test_import_boundaries.py +396 -0
  76. atdd/coder/validators/test_init_file_urns.py +593 -0
  77. atdd/coder/validators/test_preact_layer_boundaries.py +221 -0
  78. atdd/coder/validators/test_presentation_convention.py +260 -0
  79. atdd/coder/validators/test_python_architecture.py +674 -0
  80. atdd/coder/validators/test_quality_metrics.py +420 -0
  81. atdd/coder/validators/test_station_master_pattern.py +244 -0
  82. atdd/coder/validators/test_train_infrastructure.py +454 -0
  83. atdd/coder/validators/test_train_urns.py +293 -0
  84. atdd/coder/validators/test_typescript_architecture.py +616 -0
  85. atdd/coder/validators/test_usecase_structure.py +421 -0
  86. atdd/coder/validators/test_wagon_boundaries.py +586 -0
  87. atdd/conftest.py +126 -0
  88. atdd/planner/__init__.py +1 -0
  89. atdd/planner/conventions/acceptance.convention.yaml +538 -0
  90. atdd/planner/conventions/appendix.convention.yaml +187 -0
  91. atdd/planner/conventions/artifact-naming.convention.yaml +852 -0
  92. atdd/planner/conventions/component.convention.yaml +670 -0
  93. atdd/planner/conventions/criteria.convention.yaml +141 -0
  94. atdd/planner/conventions/feature.convention.yaml +371 -0
  95. atdd/planner/conventions/interface.convention.yaml +382 -0
  96. atdd/planner/conventions/steps.convention.yaml +141 -0
  97. atdd/planner/conventions/train.convention.yaml +552 -0
  98. atdd/planner/conventions/wagon.convention.yaml +275 -0
  99. atdd/planner/conventions/wmbt.convention.yaml +258 -0
  100. atdd/planner/schemas/acceptance.schema.json +336 -0
  101. atdd/planner/schemas/appendix.schema.json +78 -0
  102. atdd/planner/schemas/component.schema.json +114 -0
  103. atdd/planner/schemas/feature.schema.json +197 -0
  104. atdd/planner/schemas/train.schema.json +192 -0
  105. atdd/planner/schemas/wagon.schema.json +281 -0
  106. atdd/planner/schemas/wmbt.schema.json +59 -0
  107. atdd/planner/validators/__init__.py +0 -0
  108. atdd/planner/validators/conftest.py +5 -0
  109. atdd/planner/validators/test_draft_wagon_registry.py +374 -0
  110. atdd/planner/validators/test_plan_cross_refs.py +240 -0
  111. atdd/planner/validators/test_plan_uniqueness.py +224 -0
  112. atdd/planner/validators/test_plan_urn_resolution.py +268 -0
  113. atdd/planner/validators/test_plan_wagons.py +174 -0
  114. atdd/planner/validators/test_train_validation.py +514 -0
  115. atdd/planner/validators/test_wagon_urn_chain.py +648 -0
  116. atdd/planner/validators/test_wmbt_consistency.py +327 -0
  117. atdd/planner/validators/test_wmbt_vocabulary.py +632 -0
  118. atdd/tester/__init__.py +1 -0
  119. atdd/tester/conventions/artifact.convention.yaml +257 -0
  120. atdd/tester/conventions/contract.convention.yaml +1009 -0
  121. atdd/tester/conventions/filename.convention.yaml +555 -0
  122. atdd/tester/conventions/migration.convention.yaml +509 -0
  123. atdd/tester/conventions/red.convention.yaml +797 -0
  124. atdd/tester/conventions/routing.convention.yaml +51 -0
  125. atdd/tester/conventions/telemetry.convention.yaml +458 -0
  126. atdd/tester/schemas/a11y.tmpl.json +17 -0
  127. atdd/tester/schemas/artifact.schema.json +189 -0
  128. atdd/tester/schemas/contract.schema.json +591 -0
  129. atdd/tester/schemas/contract.tmpl.json +95 -0
  130. atdd/tester/schemas/db.tmpl.json +20 -0
  131. atdd/tester/schemas/e2e.tmpl.json +17 -0
  132. atdd/tester/schemas/edge_function.tmpl.json +17 -0
  133. atdd/tester/schemas/event.tmpl.json +17 -0
  134. atdd/tester/schemas/http.tmpl.json +19 -0
  135. atdd/tester/schemas/job.tmpl.json +18 -0
  136. atdd/tester/schemas/load.tmpl.json +21 -0
  137. atdd/tester/schemas/metric.tmpl.json +19 -0
  138. atdd/tester/schemas/pack.schema.json +139 -0
  139. atdd/tester/schemas/realtime.tmpl.json +20 -0
  140. atdd/tester/schemas/rls.tmpl.json +18 -0
  141. atdd/tester/schemas/script.tmpl.json +16 -0
  142. atdd/tester/schemas/sec.tmpl.json +18 -0
  143. atdd/tester/schemas/storage.tmpl.json +18 -0
  144. atdd/tester/schemas/telemetry.schema.json +128 -0
  145. atdd/tester/schemas/telemetry_tracking_manifest.schema.json +143 -0
  146. atdd/tester/schemas/test_filename.schema.json +194 -0
  147. atdd/tester/schemas/test_intent.schema.json +179 -0
  148. atdd/tester/schemas/unit.tmpl.json +18 -0
  149. atdd/tester/schemas/visual.tmpl.json +18 -0
  150. atdd/tester/schemas/ws.tmpl.json +17 -0
  151. atdd/tester/utils/__init__.py +0 -0
  152. atdd/tester/utils/filename.py +300 -0
  153. atdd/tester/validators/__init__.py +0 -0
  154. atdd/tester/validators/cleanup_duplicate_headers.py +116 -0
  155. atdd/tester/validators/cleanup_duplicate_headers_v2.py +135 -0
  156. atdd/tester/validators/conftest.py +5 -0
  157. atdd/tester/validators/coverage_gap_report.py +321 -0
  158. atdd/tester/validators/fix_dual_ac_references.py +179 -0
  159. atdd/tester/validators/remove_duplicate_lines.py +93 -0
  160. atdd/tester/validators/test_acceptance_urn_filename_mapping.py +359 -0
  161. atdd/tester/validators/test_acceptance_urn_separator.py +166 -0
  162. atdd/tester/validators/test_artifact_naming_category.py +307 -0
  163. atdd/tester/validators/test_contract_schema_compliance.py +706 -0
  164. atdd/tester/validators/test_contracts_structure.py +200 -0
  165. atdd/tester/validators/test_coverage_adequacy.py +797 -0
  166. atdd/tester/validators/test_dual_ac_reference.py +225 -0
  167. atdd/tester/validators/test_fixture_validity.py +372 -0
  168. atdd/tester/validators/test_isolation.py +487 -0
  169. atdd/tester/validators/test_migration_coverage.py +204 -0
  170. atdd/tester/validators/test_migration_criteria.py +276 -0
  171. atdd/tester/validators/test_migration_generation.py +116 -0
  172. atdd/tester/validators/test_python_test_naming.py +410 -0
  173. atdd/tester/validators/test_red_layer_validation.py +95 -0
  174. atdd/tester/validators/test_red_python_layer_structure.py +87 -0
  175. atdd/tester/validators/test_red_supabase_layer_structure.py +90 -0
  176. atdd/tester/validators/test_telemetry_structure.py +634 -0
  177. atdd/tester/validators/test_typescript_test_naming.py +301 -0
  178. atdd/tester/validators/test_typescript_test_structure.py +84 -0
  179. atdd-0.2.1.dist-info/METADATA +221 -0
  180. atdd-0.2.1.dist-info/RECORD +184 -0
  181. atdd-0.2.1.dist-info/WHEEL +5 -0
  182. atdd-0.2.1.dist-info/entry_points.txt +2 -0
  183. atdd-0.2.1.dist-info/licenses/LICENSE +674 -0
  184. atdd-0.2.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,293 @@
1
+ """
2
+ Test train URN validation for theme orchestrators.
3
+
4
+ Validates conventions from:
5
+ - atdd/planner/conventions/train.convention.yaml
6
+
7
+ Enforces:
8
+ - Theme orchestrators have train URNs (python/shared/{theme}.py)
9
+ - URN format: train:{theme}:{train_id}
10
+ - Train IDs exist in plan/_trains/*.yaml
11
+ - Each train file has corresponding implementation
12
+
13
+ Rationale:
14
+ Theme orchestrators in python/shared/ implement workflows defined in train specs.
15
+ URNs provide bidirectional traceability between implementation and specification.
16
+ """
17
+
18
+ import pytest
19
+ import re
20
+ import yaml
21
+ from pathlib import Path
22
+ from typing import List, Dict, Set, Tuple
23
+
24
+
25
+ # Path constants
26
+ REPO_ROOT = Path(__file__).resolve().parents[4]
27
+ PYTHON_SHARED_DIR = REPO_ROOT / "python" / "shared"
28
+ TRAINS_DIR = REPO_ROOT / "plan" / "_trains"
29
+ TRAIN_CONVENTION = REPO_ROOT / "atdd" / "planner" / "conventions" / "train.convention.yaml"
30
+
31
+
32
+ def find_theme_orchestrators() -> List[Path]:
33
+ """Find all theme orchestrator files in python/shared/."""
34
+ if not PYTHON_SHARED_DIR.exists():
35
+ return []
36
+
37
+ orchestrators = []
38
+ for py_file in PYTHON_SHARED_DIR.glob("*.py"):
39
+ # Skip __init__.py and utility files
40
+ if py_file.name in ["__init__.py", "conftest.py"]:
41
+ continue
42
+ # Skip files in subdirectories
43
+ if not py_file.parent == PYTHON_SHARED_DIR:
44
+ continue
45
+ orchestrators.append(py_file)
46
+
47
+ return orchestrators
48
+
49
+
50
+ def extract_train_urns(file_path: Path) -> List[str]:
51
+ """Extract train URNs from file header comments."""
52
+ urns = []
53
+ with open(file_path, 'r', encoding='utf-8') as f:
54
+ for line in f:
55
+ # Stop at first non-comment line after shebang
56
+ stripped = line.strip()
57
+ if not stripped:
58
+ continue
59
+ if not stripped.startswith('#'):
60
+ break
61
+
62
+ # Match: # urn: train:{theme}:{train_id}
63
+ match = re.match(r'#\s*urn:\s*train:([^:]+):(.+)', stripped)
64
+ if match:
65
+ theme = match.group(1)
66
+ train_id = match.group(2).strip()
67
+ urns.append(f"train:{theme}:{train_id}")
68
+
69
+ return urns
70
+
71
+
72
+ def find_all_train_specs() -> Dict[str, Path]:
73
+ """Find all train specification YAML files."""
74
+ train_specs = {}
75
+ if not TRAINS_DIR.exists():
76
+ return train_specs
77
+
78
+ for yaml_file in TRAINS_DIR.glob("*.yaml"):
79
+ # Train ID is the filename without extension
80
+ train_id = yaml_file.stem
81
+ train_specs[train_id] = yaml_file
82
+
83
+ return train_specs
84
+
85
+
86
+ def load_train_spec(train_file: Path) -> Dict:
87
+ """Load train specification from YAML."""
88
+ with open(train_file, 'r', encoding='utf-8') as f:
89
+ return yaml.safe_load(f)
90
+
91
+
92
+ def test_theme_orchestrators_exist():
93
+ """Theme orchestrators should exist in python/shared/ directory."""
94
+ orchestrators = find_theme_orchestrators()
95
+
96
+ assert len(orchestrators) > 0, (
97
+ "No theme orchestrators found in python/shared/. "
98
+ "Expected files like mechanic.py, match.py, etc."
99
+ )
100
+
101
+
102
+ def test_theme_orchestrators_have_train_urns():
103
+ """
104
+ Theme orchestrators must have train URN headers.
105
+
106
+ Expected format:
107
+ # urn: train:{theme}:{train_id}
108
+
109
+ Example:
110
+ # urn: train:match:3001-match-setup-standard
111
+ # urn: train:match:3004-match-completion-standard
112
+ """
113
+ orchestrators = find_theme_orchestrators()
114
+ missing_urns = []
115
+
116
+ for orch_file in orchestrators:
117
+ urns = extract_train_urns(orch_file)
118
+ if not urns:
119
+ missing_urns.append(orch_file.name)
120
+
121
+ if missing_urns:
122
+ pytest.fail(
123
+ f"\nFound {len(missing_urns)} theme orchestrators without train URNs:\n\n" +
124
+ "\n".join(f" {name}\n Missing: # urn: train:{{theme}}:{{train_id}}"
125
+ for name in missing_urns) +
126
+ "\n\nSee: atdd/planner/conventions/train.convention.yaml::theme_orchestrator_urn"
127
+ )
128
+
129
+
130
+ def test_train_urns_match_convention_format():
131
+ """
132
+ Train URNs must follow format: train:{theme}:{train_id}
133
+
134
+ Theme should match filename (e.g., match.py → train:match:...)
135
+ Train ID should match pattern: DDDD-kebab-case-name
136
+ """
137
+ orchestrators = find_theme_orchestrators()
138
+ format_violations = []
139
+
140
+ for orch_file in orchestrators:
141
+ urns = extract_train_urns(orch_file)
142
+ expected_theme = orch_file.stem # filename without .py
143
+
144
+ for urn in urns:
145
+ # Parse URN
146
+ match = re.match(r'train:([^:]+):(.+)', urn)
147
+ if not match:
148
+ format_violations.append((orch_file.name, urn, "Invalid URN format"))
149
+ continue
150
+
151
+ theme = match.group(1)
152
+ train_id = match.group(2)
153
+
154
+ # Validate theme matches filename
155
+ if theme != expected_theme:
156
+ format_violations.append((
157
+ orch_file.name,
158
+ urn,
159
+ f"Theme '{theme}' doesn't match filename '{expected_theme}.py'"
160
+ ))
161
+
162
+ # Validate train_id format (DDDD-kebab-case)
163
+ if not re.match(r'^\d{4}-[a-z][a-z0-9-]*$', train_id):
164
+ format_violations.append((
165
+ orch_file.name,
166
+ urn,
167
+ f"Train ID '{train_id}' doesn't match pattern: DDDD-kebab-case-name"
168
+ ))
169
+
170
+ if format_violations:
171
+ pytest.fail(
172
+ f"\nFound {len(format_violations)} train URN format violations:\n\n" +
173
+ "\n".join(f" {file}\n URN: {urn}\n Issue: {issue}"
174
+ for file, urn, issue in format_violations) +
175
+ "\n\nExpected format: train:{theme}:{train_id}"
176
+ "\nExample: train:match:3001-match-setup-standard"
177
+ )
178
+
179
+
180
+ def test_train_urns_reference_existing_specs():
181
+ """
182
+ Train URNs must reference train specs that exist in plan/_trains/.
183
+
184
+ For each URN train:{theme}:{train_id}, verify:
185
+ - plan/_trains/{train_id}.yaml exists
186
+ - Train spec has matching train_id field
187
+ """
188
+ orchestrators = find_theme_orchestrators()
189
+ train_specs = find_all_train_specs()
190
+ missing_specs = []
191
+
192
+ for orch_file in orchestrators:
193
+ urns = extract_train_urns(orch_file)
194
+
195
+ for urn in urns:
196
+ # Extract train_id from URN
197
+ match = re.match(r'train:([^:]+):(.+)', urn)
198
+ if not match:
199
+ continue
200
+
201
+ theme = match.group(1)
202
+ train_id = match.group(2)
203
+
204
+ # Check if train spec exists
205
+ if train_id not in train_specs:
206
+ missing_specs.append((orch_file.name, urn, train_id))
207
+
208
+ if missing_specs:
209
+ pytest.fail(
210
+ f"\nFound {len(missing_specs)} train URNs referencing non-existent specs:\n\n" +
211
+ "\n".join(f" {file}\n URN: {urn}\n Missing: plan/_trains/{train_id}.yaml"
212
+ for file, urn, train_id in missing_specs) +
213
+ "\n\nEither:\n"
214
+ " 1. Create the missing train spec in plan/_trains/\n"
215
+ " 2. Fix the URN to reference an existing train"
216
+ )
217
+
218
+
219
+ def test_train_specs_have_implementations():
220
+ """
221
+ All train specs in plan/_trains/ should have implementations.
222
+
223
+ This is a reverse check: warn about train specs without orchestrators.
224
+ Not a hard failure, but helps identify missing implementations.
225
+ """
226
+ train_specs = find_all_train_specs()
227
+ orchestrators = find_theme_orchestrators()
228
+
229
+ # Collect all implemented train IDs
230
+ implemented_trains = set()
231
+ for orch_file in orchestrators:
232
+ urns = extract_train_urns(orch_file)
233
+ for urn in urns:
234
+ match = re.match(r'train:([^:]+):(.+)', urn)
235
+ if match:
236
+ train_id = match.group(2)
237
+ implemented_trains.add(train_id)
238
+
239
+ # Find unimplemented trains
240
+ unimplemented = []
241
+ for train_id, train_file in train_specs.items():
242
+ if train_id not in implemented_trains:
243
+ # Load spec to get theme
244
+ spec = load_train_spec(train_file)
245
+ themes = spec.get('themes', [])
246
+ theme_str = themes[0] if themes else "unknown"
247
+ unimplemented.append((train_id, theme_str))
248
+
249
+ # This is just a warning, not a failure
250
+ if unimplemented:
251
+ # Group by theme
252
+ by_theme = {}
253
+ for train_id, theme in unimplemented:
254
+ if theme not in by_theme:
255
+ by_theme[theme] = []
256
+ by_theme[theme].append(train_id)
257
+
258
+ message = f"\nℹ️ Found {len(unimplemented)} train specs without implementations:\n\n"
259
+ for theme, trains in sorted(by_theme.items()):
260
+ message += f"\n {theme} theme:\n"
261
+ for train_id in sorted(trains):
262
+ message += f" - {train_id}\n"
263
+
264
+ message += (
265
+ f"\nTotal: {len(unimplemented)} trains\n"
266
+ f"Implemented: {len(implemented_trains)} trains\n"
267
+ f"Coverage: {len(implemented_trains)}/{len(train_specs)} "
268
+ f"({100*len(implemented_trains)//len(train_specs) if train_specs else 0}%)\n"
269
+ )
270
+
271
+ # Print info but don't fail
272
+ print(message)
273
+
274
+
275
+ def test_train_convention_file_exists():
276
+ """Train convention file should exist and be valid YAML."""
277
+ assert TRAIN_CONVENTION.exists(), (
278
+ f"Train convention file not found: {TRAIN_CONVENTION}\n"
279
+ "Expected: atdd/planner/conventions/train.convention.yaml"
280
+ )
281
+
282
+ # Validate it's valid YAML
283
+ with open(TRAIN_CONVENTION, 'r', encoding='utf-8') as f:
284
+ convention = yaml.safe_load(f)
285
+
286
+ assert convention is not None, "Train convention file is empty or invalid YAML"
287
+
288
+ # Check for theme_orchestrator_urn section
289
+ urn_naming = convention.get('urn_naming', {})
290
+ assert 'theme_orchestrator_urn' in urn_naming, (
291
+ "Train convention missing 'urn_naming.theme_orchestrator_urn' section\n"
292
+ "Expected format documentation for train:{theme}:{train_id}"
293
+ )