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.
@@ -4,7 +4,7 @@ Station Master Pattern Validator
4
4
  Validates that wagons follow the Station Master pattern for monolith composition:
5
5
  1. composition.py accepts optional shared dependency parameters
6
6
  2. Direct adapters exist for cross-wagon data access
7
- 3. game.py delegates to composition.py instead of duplicating wiring
7
+ 3. app.py delegates to composition.py instead of duplicating wiring
8
8
 
9
9
  Convention: atdd/coder/conventions/boundaries.convention.yaml::station_master_pattern
10
10
  """
@@ -21,6 +21,9 @@ def get_python_dir() -> Path:
21
21
  """Get the python directory path in the consumer repo."""
22
22
  return find_repo_root() / "python"
23
23
 
24
+ def resolve_server_file() -> Path:
25
+ """Resolve station master entrypoint (app.py)."""
26
+ return get_python_dir() / "app.py"
24
27
 
25
28
  def test_composition_accepts_shared_dependencies():
26
29
  """
@@ -161,12 +164,12 @@ def test_direct_adapters_exist_for_cross_wagon_clients():
161
164
 
162
165
  def test_game_py_delegates_to_composition():
163
166
  """
164
- Validate that game.py delegates wiring to wagon composition.py files
167
+ Validate that app.py delegates wiring to wagon composition.py files
165
168
  instead of duplicating wiring logic.
166
169
 
167
170
  Convention: boundaries.convention.yaml::station_master_pattern.station_master_responsibilities
168
171
 
169
- Forbidden patterns in game.py:
172
+ Forbidden patterns in app.py:
170
173
  - Creating use cases that composition.py should own
171
174
  - Directly instantiating wagon clients without delegation
172
175
 
@@ -174,14 +177,13 @@ def test_game_py_delegates_to_composition():
174
177
  - from wagon.composition import wire_api_dependencies
175
178
  - wire_api_dependencies(state_repository=..., ...)
176
179
  """
177
- python_dir = get_python_dir()
178
- game_py = python_dir / "game.py"
180
+ server_file = resolve_server_file()
179
181
 
180
- if not game_py.exists():
181
- print("game.py not found - skipping Station Master delegation check")
182
+ if not server_file.exists():
183
+ print("app.py not found - skipping Station Master delegation check")
182
184
  return
183
185
 
184
- source = game_py.read_text()
186
+ source = server_file.read_text()
185
187
 
186
188
  # Check for composition imports
187
189
  imports_composition = "from play_match.orchestrate_match.composition import wire_api_dependencies" in source
@@ -190,7 +192,7 @@ def test_game_py_delegates_to_composition():
190
192
  calls_wire_api = "wire_api_dependencies(" in source
191
193
 
192
194
  # Check for forbidden patterns (duplicated wiring)
193
- # These are patterns that should be in composition.py, not game.py
195
+ # These are patterns that should be in composition.py, not app.py
194
196
  forbidden_patterns = [
195
197
  ("PlayMatchUseCase(", "PlayMatchUseCase should be created in composition.py"),
196
198
  ("CommitStateClient(mode=", "CommitStateClient mode should be set in composition.py"),
@@ -204,7 +206,7 @@ def test_game_py_delegates_to_composition():
204
206
 
205
207
  # Report results
206
208
  print("\n" + "=" * 70)
207
- print(" Station Master Pattern: game.py Delegation")
209
+ print(" Station Master Pattern: app.py Delegation")
208
210
  print("=" * 70)
209
211
 
210
212
  print(f"\nDelegation to composition.py:")
@@ -212,19 +214,19 @@ def test_game_py_delegates_to_composition():
212
214
  print(f" {'✓' if calls_wire_api else '❌'} Calls wire_api_dependencies()")
213
215
 
214
216
  if violations:
215
- print(f"\n❌ Violations found in game.py:")
217
+ print("\n❌ Violations found in app.py:")
216
218
  for pattern, message in violations:
217
219
  print(f" ❌ {message}")
218
220
  print(f" Found: {pattern}")
219
221
 
220
222
  # This is a real validation
221
223
  assert imports_composition or not calls_wire_api, \
222
- "game.py should import wire_api_dependencies from composition.py"
224
+ "app.py should import wire_api_dependencies from composition.py"
223
225
 
224
226
  assert len(violations) == 0, \
225
- f"game.py has {len(violations)} Station Master pattern violations"
227
+ f"app.py has {len(violations)} Station Master pattern violations"
226
228
 
227
- print("\n✓ game.py follows Station Master pattern")
229
+ print("\n✓ app.py follows Station Master pattern")
228
230
 
229
231
 
230
232
  def main():
@@ -11,7 +11,7 @@ Enforces:
11
11
  - Wagons implement run_train() for train mode
12
12
  - Contract validator is real (not mock)
13
13
  - E2E tests use production TrainRunner
14
- - Station Master pattern in game.py
14
+ - Station Master pattern in app.py
15
15
 
16
16
  Train First-Class Spec v0.6 additions:
17
17
  - SPEC-TRAIN-VAL-0031: Backend runner paths
@@ -45,7 +45,7 @@ from atdd.coach.utils.config import get_train_config
45
45
  REPO_ROOT = find_repo_root()
46
46
  TRAINS_DIR = REPO_ROOT / "python" / "trains"
47
47
  WAGONS_DIR = REPO_ROOT / "python"
48
- GAME_PY = REPO_ROOT / "python" / "game.py"
48
+ APP_PY = REPO_ROOT / "python" / "app.py"
49
49
  E2E_CONFTEST = REPO_ROOT / "e2e" / "conftest.py"
50
50
  CONTRACT_VALIDATOR = REPO_ROOT / "e2e" / "shared" / "fixtures" / "contract_validator.py"
51
51
 
@@ -110,6 +110,11 @@ def extract_imports_from_file(file_path: Path) -> Set[str]:
110
110
  return imports
111
111
 
112
112
 
113
+ def resolve_server_file() -> Path:
114
+ """Resolve station master entrypoint (app.py)."""
115
+ return APP_PY
116
+
117
+
113
118
  # ============================================================================
114
119
  # TRAIN INFRASTRUCTURE TESTS
115
120
  # ============================================================================
@@ -247,45 +252,48 @@ def test_wagons_implement_run_train():
247
252
 
248
253
 
249
254
  # ============================================================================
250
- # STATION MASTER TESTS (game.py)
255
+ # STATION MASTER TESTS (app.py)
251
256
  # ============================================================================
252
257
 
253
258
  def test_game_py_imports_train_runner():
254
- """game.py must import TrainRunner (Station Master pattern)."""
255
- assert GAME_PY.exists(), f"game.py not found: {GAME_PY}"
259
+ """app.py must import TrainRunner (Station Master pattern)."""
260
+ server_file = resolve_server_file()
261
+ assert server_file.exists(), f"app.py not found: {server_file}"
256
262
 
257
- imports = extract_imports_from_file(GAME_PY)
263
+ imports = extract_imports_from_file(server_file)
258
264
 
259
265
  has_train_import = any("trains.runner import TrainRunner" in imp for imp in imports)
260
266
 
261
267
  assert has_train_import, (
262
- "game.py must import TrainRunner\n"
268
+ "app.py must import TrainRunner\n"
263
269
  "Expected: from trains.runner import TrainRunner\n"
264
270
  "See: atdd/coder/conventions/train.convention.yaml::station_master"
265
271
  )
266
272
 
267
273
 
268
274
  def test_game_py_has_journey_map():
269
- """game.py must have JOURNEY_MAP routing actions to trains."""
270
- with open(GAME_PY, 'r', encoding='utf-8') as f:
275
+ """app.py must have JOURNEY_MAP routing actions to trains."""
276
+ server_file = resolve_server_file()
277
+ with open(server_file, 'r', encoding='utf-8') as f:
271
278
  content = f.read()
272
279
 
273
280
  assert "JOURNEY_MAP" in content, (
274
- "game.py must define JOURNEY_MAP dictionary\n"
281
+ "app.py must define JOURNEY_MAP dictionary\n"
275
282
  "Expected: JOURNEY_MAP = {'action': 'train_id', ...}\n"
276
283
  "See: atdd/coder/conventions/train.convention.yaml::station_master"
277
284
  )
278
285
 
279
286
 
280
287
  def test_game_py_has_train_execution_endpoint():
281
- """game.py must have /trains/execute endpoint."""
282
- with open(GAME_PY, 'r', encoding='utf-8') as f:
288
+ """app.py must have /trains/execute endpoint."""
289
+ server_file = resolve_server_file()
290
+ with open(server_file, 'r', encoding='utf-8') as f:
283
291
  content = f.read()
284
292
 
285
293
  has_endpoint = '"/trains/execute"' in content or "'/trains/execute'" in content
286
294
 
287
295
  assert has_endpoint, (
288
- "game.py must have /trains/execute endpoint\n"
296
+ "app.py must have /trains/execute endpoint\n"
289
297
  "Expected: @app.post('/trains/execute')\n"
290
298
  "See: atdd/coder/conventions/train.convention.yaml::station_master"
291
299
  )
@@ -407,7 +415,7 @@ def test_train_convention_documents_key_patterns():
407
415
  - composition_hierarchy (with train level)
408
416
  - wagon_train_mode (run_train signature)
409
417
  - cargo_pattern (artifact flow)
410
- - station_master (game.py pattern)
418
+ - station_master (app.py pattern)
411
419
  - testing_pattern (E2E tests)
412
420
  """
413
421
  with open(TRAIN_CONVENTION, 'r', encoding='utf-8') as f:
@@ -78,8 +78,8 @@ def find_implementation_files() -> List[Path]:
78
78
  # Skip __pycache__
79
79
  if '__pycache__' in str(py_file):
80
80
  continue
81
- # Skip wagon.py, composition.py, and game.py (wagon/app-level orchestration)
82
- if py_file.name in ['wagon.py', 'composition.py', 'game.py']:
81
+ # Skip wagon.py, composition.py, and app entrypoint (wagon/app-level orchestration)
82
+ if py_file.name in ['wagon.py', 'composition.py', 'app.py']:
83
83
  continue
84
84
  # Skip shared/ directory (theme/train-level orchestration - can import across wagons)
85
85
  if '/shared/' in str(py_file):
@@ -0,0 +1,53 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://atdd.dev/schemas/tester/locale_manifest.schema.json",
4
+ "title": "Locale Manifest",
5
+ "description": "Schema for localization manifest.json - single source of truth for supported locales and namespaces (Localization Manifest Spec v1)",
6
+ "type": "object",
7
+ "required": ["reference", "locales", "namespaces"],
8
+ "properties": {
9
+ "reference": {
10
+ "type": "string",
11
+ "pattern": "^[a-z]{2}(-[A-Z]{2})?$",
12
+ "description": "Reference locale code (e.g., 'en', 'en-US'). All other locales must match this locale's keys."
13
+ },
14
+ "locales": {
15
+ "type": "array",
16
+ "minItems": 1,
17
+ "items": {
18
+ "type": "string",
19
+ "pattern": "^[a-z]{2}(-[A-Z]{2})?$"
20
+ },
21
+ "uniqueItems": true,
22
+ "description": "List of supported locale codes. Must include the reference locale."
23
+ },
24
+ "namespaces": {
25
+ "type": "array",
26
+ "minItems": 1,
27
+ "items": {
28
+ "type": "string",
29
+ "pattern": "^[a-z][a-z0-9_-]*$"
30
+ },
31
+ "uniqueItems": true,
32
+ "description": "Required namespaces that must exist for all locales (e.g., 'common', 'ui', 'errors')"
33
+ },
34
+ "optional_namespaces": {
35
+ "type": "array",
36
+ "items": {
37
+ "type": "string",
38
+ "pattern": "^[a-z][a-z0-9_-]*$"
39
+ },
40
+ "uniqueItems": true,
41
+ "description": "Optional namespaces that may not exist for all locales. If present, must match reference keys."
42
+ }
43
+ },
44
+ "additionalProperties": false,
45
+ "examples": [
46
+ {
47
+ "reference": "en",
48
+ "locales": ["en", "es", "fr", "de", "ja"],
49
+ "namespaces": ["common", "ui", "errors"],
50
+ "optional_namespaces": ["landing", "marketing"]
51
+ }
52
+ ]
53
+ }