atdd 0.6.0__py3-none-any.whl → 0.7.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.
@@ -0,0 +1,451 @@
1
+ """
2
+ Localization coverage validation (Localization Manifest Spec v1).
3
+
4
+ Validates locale files against manifest.json as single source of truth:
5
+ - LOCALE-TEST-1.1: Manifest schema compliance
6
+ - LOCALE-TEST-1.2: All locale/namespace files exist
7
+ - LOCALE-TEST-1.3: All files are valid JSON
8
+ - LOCALE-TEST-1.4: Keys match reference locale (deep comparison)
9
+ - LOCALE-TEST-1.5: Types match reference (object/array/primitive)
10
+ - LOCALE-TEST-1.6: Optional namespaces may be missing
11
+ - LOCALE-TEST-1.7: languageNames.<locale> exists in reference ui.json
12
+ """
13
+
14
+ import json
15
+ import pytest
16
+ from pathlib import Path
17
+ from typing import Any, Dict, List, Optional, Set, Tuple
18
+
19
+ import atdd
20
+ from atdd.coach.utils.locale_phase import (
21
+ LocalePhase,
22
+ should_enforce_locale,
23
+ emit_locale_warning,
24
+ )
25
+ from atdd.coach.utils.repo import find_repo_root
26
+
27
+ # Path constants
28
+ REPO_ROOT = find_repo_root()
29
+ ATDD_PKG_DIR = Path(atdd.__file__).resolve().parent
30
+
31
+
32
+ def _load_json_file(path: Path) -> Tuple[Optional[Dict], Optional[str]]:
33
+ """Load JSON file, returning (data, error_message)."""
34
+ try:
35
+ with open(path) as f:
36
+ return json.load(f), None
37
+ except json.JSONDecodeError as e:
38
+ return None, f"Invalid JSON: {e}"
39
+ except Exception as e:
40
+ return None, str(e)
41
+
42
+
43
+ def _get_all_keys(obj: Any, prefix: str = "") -> Set[str]:
44
+ """
45
+ Extract all keys from nested object using dot notation.
46
+ Ignores keys starting with underscore (private/metadata keys).
47
+ """
48
+ keys = set()
49
+ if isinstance(obj, dict):
50
+ for key, value in obj.items():
51
+ if key.startswith("_"):
52
+ continue
53
+ full_key = f"{prefix}.{key}" if prefix else key
54
+ keys.add(full_key)
55
+ keys.update(_get_all_keys(value, full_key))
56
+ return keys
57
+
58
+
59
+ def _get_type_signature(obj: Any) -> str:
60
+ """Get type signature for comparison (object/array/primitive)."""
61
+ if isinstance(obj, dict):
62
+ return "object"
63
+ elif isinstance(obj, list):
64
+ return f"array[{len(obj)}]"
65
+ elif isinstance(obj, bool):
66
+ return "boolean"
67
+ elif isinstance(obj, int):
68
+ return "number"
69
+ elif isinstance(obj, float):
70
+ return "number"
71
+ elif isinstance(obj, str):
72
+ return "string"
73
+ elif obj is None:
74
+ return "null"
75
+ return "unknown"
76
+
77
+
78
+ def _compare_types(ref_obj: Any, target_obj: Any, path: str = "") -> List[str]:
79
+ """
80
+ Compare types between reference and target objects recursively.
81
+ Returns list of type mismatch descriptions.
82
+ """
83
+ mismatches = []
84
+ ref_type = _get_type_signature(ref_obj)
85
+ target_type = _get_type_signature(target_obj)
86
+
87
+ if ref_type.startswith("array") and target_type.startswith("array"):
88
+ pass
89
+ elif ref_type != target_type:
90
+ key_display = path or "(root)"
91
+ mismatches.append(f"{key_display}: expected {ref_type}, got {target_type}")
92
+
93
+ if isinstance(ref_obj, dict) and isinstance(target_obj, dict):
94
+ for key in ref_obj:
95
+ if key.startswith("_"):
96
+ continue
97
+ if key in target_obj:
98
+ child_path = f"{path}.{key}" if path else key
99
+ mismatches.extend(_compare_types(ref_obj[key], target_obj[key], child_path))
100
+
101
+ return mismatches
102
+
103
+
104
+ @pytest.mark.locale
105
+ def test_locale_manifest_schema_compliance(locale_manifest, locale_manifest_path, load_schema):
106
+ """
107
+ LOCALE-TEST-1.1: Manifest schema compliance
108
+
109
+ Given: localization.manifest configured in .atdd/config.yaml
110
+ When: Loading the manifest file
111
+ Then: File exists and validates against locale_manifest.schema.json
112
+ """
113
+ if locale_manifest_path is None:
114
+ pytest.skip("Localization not configured (localization.manifest not in config)")
115
+
116
+ if not locale_manifest_path.exists():
117
+ msg = f"Manifest file not found: {locale_manifest_path.relative_to(REPO_ROOT)}"
118
+ if should_enforce_locale(LocalePhase.TESTER_ENFORCEMENT):
119
+ pytest.fail(msg)
120
+ else:
121
+ emit_locale_warning("LOCALE-TEST-1.1", msg)
122
+ pytest.skip(msg)
123
+
124
+ schema = load_schema("tester", "locale_manifest.schema.json")
125
+
126
+ try:
127
+ import jsonschema
128
+ jsonschema.validate(locale_manifest, schema)
129
+ except jsonschema.ValidationError as e:
130
+ msg = f"Manifest schema validation failed: {e.message}"
131
+ if should_enforce_locale(LocalePhase.TESTER_ENFORCEMENT):
132
+ pytest.fail(msg)
133
+ else:
134
+ emit_locale_warning("LOCALE-TEST-1.1", msg)
135
+ pytest.skip(msg)
136
+
137
+ if locale_manifest["reference"] not in locale_manifest["locales"]:
138
+ msg = f"Reference locale '{locale_manifest['reference']}' not in locales list"
139
+ if should_enforce_locale(LocalePhase.TESTER_ENFORCEMENT):
140
+ pytest.fail(msg)
141
+ else:
142
+ emit_locale_warning("LOCALE-TEST-1.1", msg)
143
+
144
+
145
+ @pytest.mark.locale
146
+ def test_locale_files_exist(locale_manifest, locales_dir):
147
+ """
148
+ LOCALE-TEST-1.2: All <locale>/<namespace>.json files exist
149
+
150
+ Given: Manifest with locales and namespaces
151
+ When: Checking file system
152
+ Then: All required namespace files exist for all locales
153
+ """
154
+ if locale_manifest is None:
155
+ pytest.skip("Localization not configured")
156
+
157
+ missing_files = []
158
+ locales = locale_manifest.get("locales", [])
159
+ namespaces = locale_manifest.get("namespaces", [])
160
+
161
+ for locale in locales:
162
+ locale_dir = locales_dir / locale
163
+ for namespace in namespaces:
164
+ file_path = locale_dir / f"{namespace}.json"
165
+ if not file_path.exists():
166
+ missing_files.append(f"{locale}/{namespace}.json")
167
+
168
+ if missing_files:
169
+ msg = f"Missing locale files ({len(missing_files)}):\n" + "\n".join(f" - {f}" for f in missing_files[:20])
170
+ if len(missing_files) > 20:
171
+ msg += f"\n ... and {len(missing_files) - 20} more"
172
+
173
+ if should_enforce_locale(LocalePhase.TESTER_ENFORCEMENT):
174
+ pytest.fail(msg)
175
+ else:
176
+ emit_locale_warning("LOCALE-TEST-1.2", msg)
177
+ pytest.skip(msg)
178
+
179
+
180
+ @pytest.mark.locale
181
+ def test_locale_files_valid_json(locale_manifest, locales_dir):
182
+ """
183
+ LOCALE-TEST-1.3: All locale files are valid JSON
184
+
185
+ Given: Locale namespace files
186
+ When: Parsing as JSON
187
+ Then: All files parse without errors
188
+ """
189
+ if locale_manifest is None:
190
+ pytest.skip("Localization not configured")
191
+
192
+ invalid_files = []
193
+ locales = locale_manifest.get("locales", [])
194
+ namespaces = locale_manifest.get("namespaces", [])
195
+ optional_namespaces = locale_manifest.get("optional_namespaces", [])
196
+
197
+ all_namespaces = namespaces + optional_namespaces
198
+
199
+ for locale in locales:
200
+ locale_dir = locales_dir / locale
201
+ for namespace in all_namespaces:
202
+ file_path = locale_dir / f"{namespace}.json"
203
+ if file_path.exists():
204
+ _, error = _load_json_file(file_path)
205
+ if error:
206
+ invalid_files.append(f"{locale}/{namespace}.json: {error}")
207
+
208
+ if invalid_files:
209
+ msg = f"Invalid JSON files ({len(invalid_files)}):\n" + "\n".join(f" - {f}" for f in invalid_files[:10])
210
+ if len(invalid_files) > 10:
211
+ msg += f"\n ... and {len(invalid_files) - 10} more"
212
+
213
+ if should_enforce_locale(LocalePhase.TESTER_ENFORCEMENT):
214
+ pytest.fail(msg)
215
+ else:
216
+ emit_locale_warning("LOCALE-TEST-1.3", msg)
217
+ pytest.skip(msg)
218
+
219
+
220
+ @pytest.mark.locale
221
+ def test_locale_keys_match_reference(locale_manifest, locales_dir):
222
+ """
223
+ LOCALE-TEST-1.4: Keys match reference locale (deep comparison, ignore _ prefix)
224
+
225
+ Given: Reference locale and other locales
226
+ When: Comparing keys at all nesting levels
227
+ Then: All locales have same keys as reference (ignoring _ prefixed keys)
228
+ """
229
+ if locale_manifest is None:
230
+ pytest.skip("Localization not configured")
231
+
232
+ reference = locale_manifest.get("reference")
233
+ locales = locale_manifest.get("locales", [])
234
+ namespaces = locale_manifest.get("namespaces", [])
235
+
236
+ key_mismatches = []
237
+
238
+ for namespace in namespaces:
239
+ ref_path = locales_dir / reference / f"{namespace}.json"
240
+ ref_data, ref_error = _load_json_file(ref_path)
241
+
242
+ if ref_error:
243
+ continue
244
+
245
+ ref_keys = _get_all_keys(ref_data)
246
+
247
+ for locale in locales:
248
+ if locale == reference:
249
+ continue
250
+
251
+ locale_path = locales_dir / locale / f"{namespace}.json"
252
+ locale_data, locale_error = _load_json_file(locale_path)
253
+
254
+ if locale_error:
255
+ continue
256
+
257
+ locale_keys = _get_all_keys(locale_data)
258
+
259
+ missing_keys = ref_keys - locale_keys
260
+ extra_keys = locale_keys - ref_keys
261
+
262
+ if missing_keys:
263
+ key_mismatches.append(
264
+ f"{locale}/{namespace}.json missing keys: {', '.join(sorted(missing_keys)[:5])}"
265
+ + (f" (+{len(missing_keys) - 5} more)" if len(missing_keys) > 5 else "")
266
+ )
267
+
268
+ if extra_keys:
269
+ key_mismatches.append(
270
+ f"{locale}/{namespace}.json extra keys: {', '.join(sorted(extra_keys)[:5])}"
271
+ + (f" (+{len(extra_keys) - 5} more)" if len(extra_keys) > 5 else "")
272
+ )
273
+
274
+ if key_mismatches:
275
+ msg = f"Key mismatches ({len(key_mismatches)}):\n" + "\n".join(f" - {m}" for m in key_mismatches[:15])
276
+ if len(key_mismatches) > 15:
277
+ msg += f"\n ... and {len(key_mismatches) - 15} more"
278
+
279
+ if should_enforce_locale(LocalePhase.TESTER_ENFORCEMENT):
280
+ pytest.fail(msg)
281
+ else:
282
+ emit_locale_warning("LOCALE-TEST-1.4", msg)
283
+ pytest.skip(msg)
284
+
285
+
286
+ @pytest.mark.locale
287
+ def test_locale_types_match_reference(locale_manifest, locales_dir):
288
+ """
289
+ LOCALE-TEST-1.5: Types match reference (object->object, array->array, primitive->same type)
290
+
291
+ Given: Reference locale and other locales
292
+ When: Comparing value types at each key
293
+ Then: Types match between reference and all locales
294
+ """
295
+ if locale_manifest is None:
296
+ pytest.skip("Localization not configured")
297
+
298
+ reference = locale_manifest.get("reference")
299
+ locales = locale_manifest.get("locales", [])
300
+ namespaces = locale_manifest.get("namespaces", [])
301
+
302
+ type_mismatches = []
303
+
304
+ for namespace in namespaces:
305
+ ref_path = locales_dir / reference / f"{namespace}.json"
306
+ ref_data, ref_error = _load_json_file(ref_path)
307
+
308
+ if ref_error:
309
+ continue
310
+
311
+ for locale in locales:
312
+ if locale == reference:
313
+ continue
314
+
315
+ locale_path = locales_dir / locale / f"{namespace}.json"
316
+ locale_data, locale_error = _load_json_file(locale_path)
317
+
318
+ if locale_error:
319
+ continue
320
+
321
+ mismatches = _compare_types(ref_data, locale_data)
322
+ for mismatch in mismatches:
323
+ type_mismatches.append(f"{locale}/{namespace}.json: {mismatch}")
324
+
325
+ if type_mismatches:
326
+ msg = f"Type mismatches ({len(type_mismatches)}):\n" + "\n".join(f" - {m}" for m in type_mismatches[:10])
327
+ if len(type_mismatches) > 10:
328
+ msg += f"\n ... and {len(type_mismatches) - 10} more"
329
+
330
+ if should_enforce_locale(LocalePhase.TESTER_ENFORCEMENT):
331
+ pytest.fail(msg)
332
+ else:
333
+ emit_locale_warning("LOCALE-TEST-1.5", msg)
334
+ pytest.skip(msg)
335
+
336
+
337
+ @pytest.mark.locale
338
+ def test_optional_namespaces_match_reference(locale_manifest, locales_dir):
339
+ """
340
+ LOCALE-TEST-1.6: Optional namespaces may be missing; if present, must match reference
341
+
342
+ Given: Optional namespaces in manifest
343
+ When: Checking locale files
344
+ Then: Optional namespace files may not exist, but if they do, keys must match reference
345
+ """
346
+ if locale_manifest is None:
347
+ pytest.skip("Localization not configured")
348
+
349
+ optional_namespaces = locale_manifest.get("optional_namespaces", [])
350
+ if not optional_namespaces:
351
+ pytest.skip("No optional namespaces defined")
352
+
353
+ reference = locale_manifest.get("reference")
354
+ locales = locale_manifest.get("locales", [])
355
+
356
+ key_mismatches = []
357
+
358
+ for namespace in optional_namespaces:
359
+ ref_path = locales_dir / reference / f"{namespace}.json"
360
+ ref_data, ref_error = _load_json_file(ref_path)
361
+
362
+ if ref_error or ref_data is None:
363
+ continue
364
+
365
+ ref_keys = _get_all_keys(ref_data)
366
+
367
+ for locale in locales:
368
+ if locale == reference:
369
+ continue
370
+
371
+ locale_path = locales_dir / locale / f"{namespace}.json"
372
+ if not locale_path.exists():
373
+ continue
374
+
375
+ locale_data, locale_error = _load_json_file(locale_path)
376
+ if locale_error:
377
+ continue
378
+
379
+ locale_keys = _get_all_keys(locale_data)
380
+
381
+ missing_keys = ref_keys - locale_keys
382
+ extra_keys = locale_keys - ref_keys
383
+
384
+ if missing_keys:
385
+ key_mismatches.append(
386
+ f"{locale}/{namespace}.json (optional) missing keys: {', '.join(sorted(missing_keys)[:5])}"
387
+ )
388
+
389
+ if extra_keys:
390
+ key_mismatches.append(
391
+ f"{locale}/{namespace}.json (optional) extra keys: {', '.join(sorted(extra_keys)[:5])}"
392
+ )
393
+
394
+ if key_mismatches:
395
+ msg = f"Optional namespace key mismatches ({len(key_mismatches)}):\n" + "\n".join(f" - {m}" for m in key_mismatches[:10])
396
+ if len(key_mismatches) > 10:
397
+ msg += f"\n ... and {len(key_mismatches) - 10} more"
398
+
399
+ if should_enforce_locale(LocalePhase.TESTER_ENFORCEMENT):
400
+ pytest.fail(msg)
401
+ else:
402
+ emit_locale_warning("LOCALE-TEST-1.6", msg)
403
+ pytest.skip(msg)
404
+
405
+
406
+ @pytest.mark.locale
407
+ def test_language_names_complete(locale_manifest, locales_dir):
408
+ """
409
+ LOCALE-TEST-1.7: languageNames.<locale> exists in reference ui.json
410
+
411
+ Given: Manifest with locales list
412
+ When: Checking reference ui.json for languageNames
413
+ Then: Each locale in manifest has a corresponding languageNames entry
414
+ """
415
+ if locale_manifest is None:
416
+ pytest.skip("Localization not configured")
417
+
418
+ reference = locale_manifest.get("reference")
419
+ locales = locale_manifest.get("locales", [])
420
+ namespaces = locale_manifest.get("namespaces", [])
421
+
422
+ if "ui" not in namespaces:
423
+ pytest.skip("No 'ui' namespace configured - skipping languageNames check")
424
+
425
+ ui_path = locales_dir / reference / "ui.json"
426
+ ui_data, ui_error = _load_json_file(ui_path)
427
+
428
+ if ui_error:
429
+ pytest.skip(f"Cannot read reference ui.json: {ui_error}")
430
+
431
+ language_names = ui_data.get("languageNames", {})
432
+ if not language_names:
433
+ msg = "Reference ui.json missing 'languageNames' object"
434
+ if should_enforce_locale(LocalePhase.TESTER_ENFORCEMENT):
435
+ pytest.fail(msg)
436
+ else:
437
+ emit_locale_warning("LOCALE-TEST-1.7", msg)
438
+ pytest.skip(msg)
439
+
440
+ missing_names = []
441
+ for locale in locales:
442
+ if locale not in language_names:
443
+ missing_names.append(locale)
444
+
445
+ if missing_names:
446
+ msg = f"Missing languageNames entries for locales: {', '.join(missing_names)}"
447
+ if should_enforce_locale(LocalePhase.TESTER_ENFORCEMENT):
448
+ pytest.fail(msg)
449
+ else:
450
+ emit_locale_warning("LOCALE-TEST-1.7", msg)
451
+ pytest.skip(msg)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atdd
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: ATDD Platform - Acceptance Test Driven Development toolkit
5
5
  License: MIT
6
6
  Requires-Python: >=3.10
@@ -25,19 +25,20 @@ atdd/coach/commands/tests/test_telemetry_array_validation.py,sha256=WK5ZXvR1avlz
25
25
  atdd/coach/conventions/session.convention.yaml,sha256=1wCxQ_Y2Wb2080Xt2JZs0_WsV8_4SC0Tq87G_BCGdiE,26049
26
26
  atdd/coach/overlays/__init__.py,sha256=2lMiMSgfLJ3YHLpbzNI5B88AdQxiMEwjIfsWWb8t3To,123
27
27
  atdd/coach/overlays/claude.md,sha256=33mhpqhmsRhCtdWlU7cMXAJDsaVra9uBBK8URV8OtQA,101
28
- atdd/coach/schemas/config.schema.json,sha256=XcqpISpV-57Kk7HdBXz_4K0jqj_SdKgBHy9VyVpNRD8,4191
28
+ atdd/coach/schemas/config.schema.json,sha256=47cFGE5juBv9ewhtgrNir4b6I9imIIo8VjoD9yvASf4,4578
29
29
  atdd/coach/schemas/manifest.schema.json,sha256=WO13-YF_FgH1awh96khCtk-112b6XSC24anlY3B7GjY,2885
30
- atdd/coach/templates/ATDD.md,sha256=MLbrVbCETJre4c05d5FXGuf6W95Hz9E0jpE4RI9r4cg,13237
30
+ atdd/coach/templates/ATDD.md,sha256=h_oPpKLX7nuafC0VAoKCnkM2-kGQeksR34QWlGyfMwU,13236
31
31
  atdd/coach/templates/SESSION-TEMPLATE.md,sha256=cGT_0x5KLbPHOCiuM8evLGpWKIlR-aggqxiBtbjSJoo,9478
32
32
  atdd/coach/utils/__init__.py,sha256=7Jbo-heJEKSAn6I0s35z_2S4R8qGZ48PL6a2IntcNYg,148
33
33
  atdd/coach/utils/config.py,sha256=6XXaaeVfjTrJwdaR0IZ6Kf1-1ZHhaCVLO5pNx_A2el4,3320
34
34
  atdd/coach/utils/coverage_phase.py,sha256=14CzGiTEeb-Z-CMYnJjx1-4dn3LbQVJUlFr_-1bKVMc,3250
35
+ atdd/coach/utils/locale_phase.py,sha256=S6eORhvj2N412BOY4QFQGcLA_twsr_D4vviy4X-KDNo,3236
35
36
  atdd/coach/utils/repo.py,sha256=0kiF5WpVTen0nO14u5T0RflznZhgGco2i9CwKobOh38,3757
36
37
  atdd/coach/utils/train_spec_phase.py,sha256=Mk8CiMoO6jb-VGttHgI20KIG26r9cjSz4gDfk01q1M0,3025
37
38
  atdd/coach/utils/graph/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
39
  atdd/coach/utils/graph/urn.py,sha256=O2AHIB_CmmMUvXzyejc_oFReNW_rOcw7m4qaqSYcnNQ,33558
39
40
  atdd/coach/validators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
- atdd/coach/validators/shared_fixtures.py,sha256=q-J39x0pxdEWJmRWe45i6VyYy8-0MHsi9ElMAvDx3MA,19474
41
+ atdd/coach/validators/shared_fixtures.py,sha256=Ia3B2fUW-aKibwVPF6RnRemtu3R_Dfb-2MvPVNitgxg,20931
41
42
  atdd/coach/validators/test_enrich_wagon_registry.py,sha256=WeTwYJqoNY6mEYc-QAvQo7YVagSOjaNKxB6Q6dpWqIM,6561
42
43
  atdd/coach/validators/test_registry.py,sha256=ffN70yA_1xxL3R8gdpGbY2M8dQXyuajIZhBZ-ylNiNs,17845
43
44
  atdd/coach/validators/test_release_versioning.py,sha256=B40DfbtrSGguPc537zXmjT75hhySfocWLzJWqOKZQcU,5678
@@ -48,8 +49,8 @@ atdd/coach/validators/test_update_feature_paths.py,sha256=zOKVDgEIpncSJwDh_shyyo
48
49
  atdd/coach/validators/test_validate_contract_consumers.py,sha256=b01yam_GwAERF6YaFmUV6Bd7SNMWQkUKBfNVvplbEcU,12613
49
50
  atdd/coder/__init__.py,sha256=Rmi5S7Pzx7qsRe5MC66GduNGmkssWWnElkVvvNHFDgU,45
50
51
  atdd/coder/conventions/adapter.recipe.yaml,sha256=Ss9OJJ-FEP8TU_D-N6X_1nO-Karb-Cg5OopL9_gLjMI,3039
51
- atdd/coder/conventions/backend.convention.yaml,sha256=fWUUph6iGeItRBrL-Hhma2e7dLSwzI4NgpAkb7985Wg,16600
52
- atdd/coder/conventions/boundaries.convention.yaml,sha256=FTv9V-67g-CM2SvopQuA8sMXufiIDRJT4CAIzKcDPYc,25513
52
+ atdd/coder/conventions/backend.convention.yaml,sha256=OJkBZUPaGFfKmO8UPslucbA6RZtsctvoywfMLFw5irg,16599
53
+ atdd/coder/conventions/boundaries.convention.yaml,sha256=uMahpzwNru8KuGDllXrajD5VQ5f62GPsz0nMsfgaAQw,25504
53
54
  atdd/coder/conventions/commons.convention.yaml,sha256=zwfO4dYNrFjy4MLZ9RLK3Yd7gotTWYdFfXMPp3EfU0A,17309
54
55
  atdd/coder/conventions/complexity.recipe.yaml,sha256=fjVnsb0kGGstTDKTaeYO_1XJ3408wE0lheAxf02Hm3s,3636
55
56
  atdd/coder/conventions/component-naming.convention.yaml,sha256=w1LKJM7lhmpFqBCpRAvx03Z_Ugujd3P6rFfmp5xbhg0,7444
@@ -59,11 +60,11 @@ atdd/coder/conventions/design.recipe.yaml,sha256=LP2S504JpfzwoL3jPdSmYKMmg-_Bzlo
59
60
  atdd/coder/conventions/dto.convention.yaml,sha256=aawWJVwoXU5yV73-rpHDSYtYMfm0XSWRRMujGCQrtYY,23700
60
61
  atdd/coder/conventions/frontend.convention.yaml,sha256=2qBtLbqo1SrM17mXMlbZg4xlNAlq0_Kj2n18XphsQl8,19748
61
62
  atdd/coder/conventions/green.convention.yaml,sha256=jq-Y5yHizf8o6NSJMD-9BPGrAv-otXRRjHikSGqn3aI,39091
62
- atdd/coder/conventions/presentation.convention.yaml,sha256=YTyRdOMYhfsinYA0f7wWezzZ-jFPNdmgMfawb_nKybE,21893
63
+ atdd/coder/conventions/presentation.convention.yaml,sha256=P8_J6Bh58AkeoGfsG91KPBO7pXJOvaYeCxp-JleX2Tw,21885
63
64
  atdd/coder/conventions/refactor.convention.yaml,sha256=kpETeWqZRlahV55c9bEf1bLOokL_DDR0jY4mffKfY_c,19292
64
65
  atdd/coder/conventions/technology.convention.yaml,sha256=OU6_XArKTKtLoI9c67GzAM3tjWj7Qx1qrW7uQgIXvVY,13826
65
66
  atdd/coder/conventions/thinness.recipe.yaml,sha256=4FZplEJQA93qsvpJ4QPCn91duWtTL_V_CsqSgIFfBSo,2664
66
- atdd/coder/conventions/train.convention.yaml,sha256=HAMYLzqwDEHMefJD2eSjAwzQebjNVL-BhUfrniiBydI,11789
67
+ atdd/coder/conventions/train.convention.yaml,sha256=saHeqQVzKkIjD1XxjTXApv5tGsQ_CPXFtsU8qbsvaag,11829
67
68
  atdd/coder/conventions/verification.protocol.yaml,sha256=tYOId2A8xKoTqjcrKN1RNUK20_f0_e9GeVm_GBKrXMQ,1523
68
69
  atdd/coder/conventions/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
70
  atdd/coder/conventions/tests/test_adapter_recipe.py,sha256=293CU9rj7z6bj_1Wh02VhVCslaHfK-KeMJFgKgK7cvM,9370
@@ -84,18 +85,19 @@ atdd/coder/validators/test_green_layer_dependencies.py,sha256=8TWuTD3RrDBqp9LUCS
84
85
  atdd/coder/validators/test_green_python_layer_structure.py,sha256=WXo1VA5-WprgtAQgC8ekhuIRJR47b_qgTYTjPn7svms,4389
85
86
  atdd/coder/validators/test_green_supabase_layer_structure.py,sha256=cLj85acLX6Knewk9AWbiJDwEzoaE-NBNollyJCvRUD0,4371
86
87
  atdd/coder/validators/test_hierarchy_coverage.py,sha256=1JuaiO4k-v3_rL5x4-MPsKajAv-K5cTzevW5eS4cC-U,12240
88
+ atdd/coder/validators/test_i18n_runtime.py,sha256=pmx1nhvOS1nk2xa1E5Lm8yPgpylNhV6AjDECeIzN4Pg,5575
87
89
  atdd/coder/validators/test_import_boundaries.py,sha256=3kzVMKIwZU9FcS0YLU8gsuHSDRlRr2rU3UPgoraV2FU,11811
88
90
  atdd/coder/validators/test_init_file_urns.py,sha256=uDJ2MgfJNFcjzoIKItn73n9V08m3ZBkt1SAZgHWdXPs,17914
89
91
  atdd/coder/validators/test_preact_layer_boundaries.py,sha256=rabXY9gif3b8QH9_Kz5Pu6FZSvDpfQKDm_SKjdpLD44,7766
90
- atdd/coder/validators/test_presentation_convention.py,sha256=ceAtg_sTEJiPkHVfnwBEDTzTH44nKQIiukIEvZz8r18,10037
92
+ atdd/coder/validators/test_presentation_convention.py,sha256=LKmiMStrR6uqcyYOyBr08EHyeGHHiN2UvBpO1EgUzZM,10040
91
93
  atdd/coder/validators/test_python_architecture.py,sha256=USnSujKVu7_BC2ij-pTSHyiFi4iBMBemIPR7oXYQ3B4,25243
92
94
  atdd/coder/validators/test_quality_metrics.py,sha256=vLWPfL0x9sfCvKXO4uECW61RnJTsjbmuEUjCsBcUmuA,12551
93
- atdd/coder/validators/test_station_master_pattern.py,sha256=biYwXAftpXdhIhDjn2RrVTdEdvTWPW5ddm2gQubJBXQ,9280
94
- atdd/coder/validators/test_train_infrastructure.py,sha256=CXOSn_CPk3wJhEVNYztSdiV_ecIgnJkulD-xWivregE,24424
95
+ atdd/coder/validators/test_station_master_pattern.py,sha256=R0VajDIabKQtXKFCZPzWaY-euTcMLr0ECIim-s1_zX8,9374
96
+ atdd/coder/validators/test_train_infrastructure.py,sha256=_91k0d1Q7RkQZb-2N8fD_eRXe-oMMQGhQ_u_41ytHlU,24661
95
97
  atdd/coder/validators/test_train_urns.py,sha256=TvSNHbvG8SAtAPaS2K8mMgzE8GbJtrTaF4iEgkJEIIk,10099
96
98
  atdd/coder/validators/test_typescript_architecture.py,sha256=gi76IwlqX_MpWsr8CXWH7v16KAoud-1OY92iuN-0BpU,22176
97
99
  atdd/coder/validators/test_usecase_structure.py,sha256=nAeqmLWRCstXT8fDuxqk6iVcH4TLHjTvlaGDpanD9sM,14077
98
- atdd/coder/validators/test_wagon_boundaries.py,sha256=SgqGfQKR91qf7fkPl7Ye1RKQ5CcqoyukAdWK3xqV3Ns,20690
100
+ atdd/coder/validators/test_wagon_boundaries.py,sha256=lifrhazbW2fJOwgU0vnNCjXZGfknUh6pa7err2TYwyY,20696
99
101
  atdd/planner/__init__.py,sha256=ZXzKPNqP_JgEeFnwwUAKfIwz2e1i2gwrmz1SyC65sl4,47
100
102
  atdd/planner/conventions/acceptance.convention.yaml,sha256=q_1hm-uJ2Sbh6VqGHQ543L9NTnOuS2UGWT-n9uAdfmM,21918
101
103
  atdd/planner/conventions/appendix.convention.yaml,sha256=wuv9mnSZ-L1vUp2xx0OmyOsYurApitsmO_MilLVGcOc,9394
@@ -149,6 +151,7 @@ atdd/tester/schemas/event.tmpl.json,sha256=3-7cSaesuPcVZP5NENrg-GnDRQPbwkWB_1ISI
149
151
  atdd/tester/schemas/http.tmpl.json,sha256=zrynEO18HmEXanQDVk3XZNHNOp1lUyVKXTR-f7_KAGI,818
150
152
  atdd/tester/schemas/job.tmpl.json,sha256=7HtX-esAb9asB-9uNyuWDRKE-HWovlNnuYYllSzjJDg,926
151
153
  atdd/tester/schemas/load.tmpl.json,sha256=wSPfGXTEVQrgsqVXihwiDIKWS_QErkXlh2WdVJwLqoc,971
154
+ atdd/tester/schemas/locale_manifest.schema.json,sha256=4-A25522C4Bdy7Y3I9USqsk4K97hkv3rTr60-dcVZAk,1736
152
155
  atdd/tester/schemas/metric.tmpl.json,sha256=jHTQgAsnVXWPlQipjnu79eqZwvHcDV5giaoN9w1HZdY,949
153
156
  atdd/tester/schemas/pack.schema.json,sha256=pQfbvaB5Kl1SFCewz60zaTRviuZm-gM_9QCYipJWa3w,4575
154
157
  atdd/tester/schemas/realtime.tmpl.json,sha256=c9Sbkqk6vFUhzTWI2slfyTfEwJ0ltFhQrtjPabRLtmo,1141
@@ -183,6 +186,7 @@ atdd/tester/validators/test_dual_ac_reference.py,sha256=LDhIqXyVxgWVCgj7FneDTLt6
183
186
  atdd/tester/validators/test_fixture_validity.py,sha256=Fp4AWwhvZlos1ik_d7NbP030Qq-klZLnCmc12ylptqs,12101
184
187
  atdd/tester/validators/test_hierarchy_coverage.py,sha256=o2jd3dsqvqtqQ3I3RQa4FGoYIJE0vArUIISurDxYTgw,21060
185
188
  atdd/tester/validators/test_isolation.py,sha256=NYrqJcVDZH0SDRWHlPdazG6THT4w3XEvz_xn4PBxU4E,16489
189
+ atdd/tester/validators/test_locale_coverage.py,sha256=amnZAx5wTvF6mGbclvu40n0XhYBKLl97SnBVkbLMv1E,15487
186
190
  atdd/tester/validators/test_migration_coverage.py,sha256=LOx0L9KLH4gVisNHXhxKrzHLgCgj4PVZxeZ-2gg-SQk,7344
187
191
  atdd/tester/validators/test_migration_criteria.py,sha256=YDGvWjkVSjUVVNv4RJWLdy4iLoG1EXzmm_ficD0Gt3Q,7896
188
192
  atdd/tester/validators/test_migration_generation.py,sha256=wpTmuxvM13OfSgC-3SJBdtB2XaPjBYyD-jXVYWi7Z9Q,4064
@@ -196,9 +200,9 @@ atdd/tester/validators/test_train_frontend_e2e.py,sha256=fpfUwTbAWzuqxbVKoaFw-ab
196
200
  atdd/tester/validators/test_train_frontend_python.py,sha256=KK2U3oNFWLyBK7YHC0fU7shR05k93gVcO762AI8Q3pw,9018
197
201
  atdd/tester/validators/test_typescript_test_naming.py,sha256=E-TyGv_GVlTfsbyuxrtv9sOWSZS_QcpH6rrJFbWoeeU,11280
198
202
  atdd/tester/validators/test_typescript_test_structure.py,sha256=eV89SD1RaKtchBZupqhnJmaruoROosf3LwB4Fwe4UJI,2612
199
- atdd-0.6.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
200
- atdd-0.6.0.dist-info/METADATA,sha256=HNjWP1nADO8JkscZuxdkS5sd0v_r7t25J3WmkO6n2JM,8716
201
- atdd-0.6.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
202
- atdd-0.6.0.dist-info/entry_points.txt,sha256=-C3yrA1WQQfN3iuGmSzPapA5cKVBEYU5Q1HUffSJTbY,38
203
- atdd-0.6.0.dist-info/top_level.txt,sha256=VKkf6Uiyrm4RS6ULCGM-v8AzYN8K2yg8SMqwJLoO-xs,5
204
- atdd-0.6.0.dist-info/RECORD,,
203
+ atdd-0.7.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
204
+ atdd-0.7.0.dist-info/METADATA,sha256=EEjcJY5c8XfUKF8ebYBc1o3mjFrJqqJldicW2coK-iQ,8716
205
+ atdd-0.7.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
206
+ atdd-0.7.0.dist-info/entry_points.txt,sha256=-C3yrA1WQQfN3iuGmSzPapA5cKVBEYU5Q1HUffSJTbY,38
207
+ atdd-0.7.0.dist-info/top_level.txt,sha256=VKkf6Uiyrm4RS6ULCGM-v8AzYN8K2yg8SMqwJLoO-xs,5
208
+ atdd-0.7.0.dist-info/RECORD,,
File without changes