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.
- atdd/__init__.py +6 -0
- atdd/__main__.py +4 -0
- atdd/cli.py +404 -0
- atdd/coach/__init__.py +0 -0
- atdd/coach/commands/__init__.py +0 -0
- atdd/coach/commands/add_persistence_metadata.py +215 -0
- atdd/coach/commands/analyze_migrations.py +188 -0
- atdd/coach/commands/consumers.py +720 -0
- atdd/coach/commands/infer_governance_status.py +149 -0
- atdd/coach/commands/initializer.py +177 -0
- atdd/coach/commands/interface.py +1078 -0
- atdd/coach/commands/inventory.py +565 -0
- atdd/coach/commands/migration.py +240 -0
- atdd/coach/commands/registry.py +1560 -0
- atdd/coach/commands/session.py +430 -0
- atdd/coach/commands/sync.py +405 -0
- atdd/coach/commands/test_interface.py +399 -0
- atdd/coach/commands/test_runner.py +141 -0
- atdd/coach/commands/tests/__init__.py +1 -0
- atdd/coach/commands/tests/test_telemetry_array_validation.py +235 -0
- atdd/coach/commands/traceability.py +4264 -0
- atdd/coach/conventions/session.convention.yaml +754 -0
- atdd/coach/overlays/__init__.py +2 -0
- atdd/coach/overlays/claude.md +2 -0
- atdd/coach/schemas/config.schema.json +34 -0
- atdd/coach/schemas/manifest.schema.json +101 -0
- atdd/coach/templates/ATDD.md +282 -0
- atdd/coach/templates/SESSION-TEMPLATE.md +327 -0
- atdd/coach/utils/__init__.py +0 -0
- atdd/coach/utils/graph/__init__.py +0 -0
- atdd/coach/utils/graph/urn.py +875 -0
- atdd/coach/validators/__init__.py +0 -0
- atdd/coach/validators/shared_fixtures.py +365 -0
- atdd/coach/validators/test_enrich_wagon_registry.py +167 -0
- atdd/coach/validators/test_registry.py +575 -0
- atdd/coach/validators/test_session_validation.py +1183 -0
- atdd/coach/validators/test_traceability.py +448 -0
- atdd/coach/validators/test_update_feature_paths.py +108 -0
- atdd/coach/validators/test_validate_contract_consumers.py +297 -0
- atdd/coder/__init__.py +1 -0
- atdd/coder/conventions/adapter.recipe.yaml +88 -0
- atdd/coder/conventions/backend.convention.yaml +460 -0
- atdd/coder/conventions/boundaries.convention.yaml +666 -0
- atdd/coder/conventions/commons.convention.yaml +460 -0
- atdd/coder/conventions/complexity.recipe.yaml +109 -0
- atdd/coder/conventions/component-naming.convention.yaml +178 -0
- atdd/coder/conventions/design.convention.yaml +327 -0
- atdd/coder/conventions/design.recipe.yaml +273 -0
- atdd/coder/conventions/dto.convention.yaml +660 -0
- atdd/coder/conventions/frontend.convention.yaml +542 -0
- atdd/coder/conventions/green.convention.yaml +1012 -0
- atdd/coder/conventions/presentation.convention.yaml +587 -0
- atdd/coder/conventions/refactor.convention.yaml +535 -0
- atdd/coder/conventions/technology.convention.yaml +206 -0
- atdd/coder/conventions/tests/__init__.py +0 -0
- atdd/coder/conventions/tests/test_adapter_recipe.py +302 -0
- atdd/coder/conventions/tests/test_complexity_recipe.py +289 -0
- atdd/coder/conventions/tests/test_component_taxonomy.py +278 -0
- atdd/coder/conventions/tests/test_component_urn_naming.py +165 -0
- atdd/coder/conventions/tests/test_thinness_recipe.py +286 -0
- atdd/coder/conventions/thinness.recipe.yaml +82 -0
- atdd/coder/conventions/train.convention.yaml +325 -0
- atdd/coder/conventions/verification.protocol.yaml +53 -0
- atdd/coder/schemas/design_system.schema.json +361 -0
- atdd/coder/validators/__init__.py +0 -0
- atdd/coder/validators/test_commons_structure.py +485 -0
- atdd/coder/validators/test_complexity.py +416 -0
- atdd/coder/validators/test_cross_language_consistency.py +431 -0
- atdd/coder/validators/test_design_system_compliance.py +413 -0
- atdd/coder/validators/test_dto_testing_patterns.py +268 -0
- atdd/coder/validators/test_green_cross_stack_layers.py +168 -0
- atdd/coder/validators/test_green_layer_dependencies.py +148 -0
- atdd/coder/validators/test_green_python_layer_structure.py +103 -0
- atdd/coder/validators/test_green_supabase_layer_structure.py +103 -0
- atdd/coder/validators/test_import_boundaries.py +396 -0
- atdd/coder/validators/test_init_file_urns.py +593 -0
- atdd/coder/validators/test_preact_layer_boundaries.py +221 -0
- atdd/coder/validators/test_presentation_convention.py +260 -0
- atdd/coder/validators/test_python_architecture.py +674 -0
- atdd/coder/validators/test_quality_metrics.py +420 -0
- atdd/coder/validators/test_station_master_pattern.py +244 -0
- atdd/coder/validators/test_train_infrastructure.py +454 -0
- atdd/coder/validators/test_train_urns.py +293 -0
- atdd/coder/validators/test_typescript_architecture.py +616 -0
- atdd/coder/validators/test_usecase_structure.py +421 -0
- atdd/coder/validators/test_wagon_boundaries.py +586 -0
- atdd/conftest.py +126 -0
- atdd/planner/__init__.py +1 -0
- atdd/planner/conventions/acceptance.convention.yaml +538 -0
- atdd/planner/conventions/appendix.convention.yaml +187 -0
- atdd/planner/conventions/artifact-naming.convention.yaml +852 -0
- atdd/planner/conventions/component.convention.yaml +670 -0
- atdd/planner/conventions/criteria.convention.yaml +141 -0
- atdd/planner/conventions/feature.convention.yaml +371 -0
- atdd/planner/conventions/interface.convention.yaml +382 -0
- atdd/planner/conventions/steps.convention.yaml +141 -0
- atdd/planner/conventions/train.convention.yaml +552 -0
- atdd/planner/conventions/wagon.convention.yaml +275 -0
- atdd/planner/conventions/wmbt.convention.yaml +258 -0
- atdd/planner/schemas/acceptance.schema.json +336 -0
- atdd/planner/schemas/appendix.schema.json +78 -0
- atdd/planner/schemas/component.schema.json +114 -0
- atdd/planner/schemas/feature.schema.json +197 -0
- atdd/planner/schemas/train.schema.json +192 -0
- atdd/planner/schemas/wagon.schema.json +281 -0
- atdd/planner/schemas/wmbt.schema.json +59 -0
- atdd/planner/validators/__init__.py +0 -0
- atdd/planner/validators/conftest.py +5 -0
- atdd/planner/validators/test_draft_wagon_registry.py +374 -0
- atdd/planner/validators/test_plan_cross_refs.py +240 -0
- atdd/planner/validators/test_plan_uniqueness.py +224 -0
- atdd/planner/validators/test_plan_urn_resolution.py +268 -0
- atdd/planner/validators/test_plan_wagons.py +174 -0
- atdd/planner/validators/test_train_validation.py +514 -0
- atdd/planner/validators/test_wagon_urn_chain.py +648 -0
- atdd/planner/validators/test_wmbt_consistency.py +327 -0
- atdd/planner/validators/test_wmbt_vocabulary.py +632 -0
- atdd/tester/__init__.py +1 -0
- atdd/tester/conventions/artifact.convention.yaml +257 -0
- atdd/tester/conventions/contract.convention.yaml +1009 -0
- atdd/tester/conventions/filename.convention.yaml +555 -0
- atdd/tester/conventions/migration.convention.yaml +509 -0
- atdd/tester/conventions/red.convention.yaml +797 -0
- atdd/tester/conventions/routing.convention.yaml +51 -0
- atdd/tester/conventions/telemetry.convention.yaml +458 -0
- atdd/tester/schemas/a11y.tmpl.json +17 -0
- atdd/tester/schemas/artifact.schema.json +189 -0
- atdd/tester/schemas/contract.schema.json +591 -0
- atdd/tester/schemas/contract.tmpl.json +95 -0
- atdd/tester/schemas/db.tmpl.json +20 -0
- atdd/tester/schemas/e2e.tmpl.json +17 -0
- atdd/tester/schemas/edge_function.tmpl.json +17 -0
- atdd/tester/schemas/event.tmpl.json +17 -0
- atdd/tester/schemas/http.tmpl.json +19 -0
- atdd/tester/schemas/job.tmpl.json +18 -0
- atdd/tester/schemas/load.tmpl.json +21 -0
- atdd/tester/schemas/metric.tmpl.json +19 -0
- atdd/tester/schemas/pack.schema.json +139 -0
- atdd/tester/schemas/realtime.tmpl.json +20 -0
- atdd/tester/schemas/rls.tmpl.json +18 -0
- atdd/tester/schemas/script.tmpl.json +16 -0
- atdd/tester/schemas/sec.tmpl.json +18 -0
- atdd/tester/schemas/storage.tmpl.json +18 -0
- atdd/tester/schemas/telemetry.schema.json +128 -0
- atdd/tester/schemas/telemetry_tracking_manifest.schema.json +143 -0
- atdd/tester/schemas/test_filename.schema.json +194 -0
- atdd/tester/schemas/test_intent.schema.json +179 -0
- atdd/tester/schemas/unit.tmpl.json +18 -0
- atdd/tester/schemas/visual.tmpl.json +18 -0
- atdd/tester/schemas/ws.tmpl.json +17 -0
- atdd/tester/utils/__init__.py +0 -0
- atdd/tester/utils/filename.py +300 -0
- atdd/tester/validators/__init__.py +0 -0
- atdd/tester/validators/cleanup_duplicate_headers.py +116 -0
- atdd/tester/validators/cleanup_duplicate_headers_v2.py +135 -0
- atdd/tester/validators/conftest.py +5 -0
- atdd/tester/validators/coverage_gap_report.py +321 -0
- atdd/tester/validators/fix_dual_ac_references.py +179 -0
- atdd/tester/validators/remove_duplicate_lines.py +93 -0
- atdd/tester/validators/test_acceptance_urn_filename_mapping.py +359 -0
- atdd/tester/validators/test_acceptance_urn_separator.py +166 -0
- atdd/tester/validators/test_artifact_naming_category.py +307 -0
- atdd/tester/validators/test_contract_schema_compliance.py +706 -0
- atdd/tester/validators/test_contracts_structure.py +200 -0
- atdd/tester/validators/test_coverage_adequacy.py +797 -0
- atdd/tester/validators/test_dual_ac_reference.py +225 -0
- atdd/tester/validators/test_fixture_validity.py +372 -0
- atdd/tester/validators/test_isolation.py +487 -0
- atdd/tester/validators/test_migration_coverage.py +204 -0
- atdd/tester/validators/test_migration_criteria.py +276 -0
- atdd/tester/validators/test_migration_generation.py +116 -0
- atdd/tester/validators/test_python_test_naming.py +410 -0
- atdd/tester/validators/test_red_layer_validation.py +95 -0
- atdd/tester/validators/test_red_python_layer_structure.py +87 -0
- atdd/tester/validators/test_red_supabase_layer_structure.py +90 -0
- atdd/tester/validators/test_telemetry_structure.py +634 -0
- atdd/tester/validators/test_typescript_test_naming.py +301 -0
- atdd/tester/validators/test_typescript_test_structure.py +84 -0
- atdd-0.2.1.dist-info/METADATA +221 -0
- atdd-0.2.1.dist-info/RECORD +184 -0
- atdd-0.2.1.dist-info/WHEEL +5 -0
- atdd-0.2.1.dist-info/entry_points.txt +2 -0
- atdd-0.2.1.dist-info/licenses/LICENSE +674 -0
- atdd-0.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
schema_version: "1.0.0"
|
|
2
|
+
convention_id: "coder.train"
|
|
3
|
+
name: "Train Composition Root Convention"
|
|
4
|
+
description: "Production orchestration layer for executing user journeys across wagons"
|
|
5
|
+
|
|
6
|
+
rationale: |
|
|
7
|
+
Trains are PRODUCTION orchestration, not test infrastructure. They execute user journeys
|
|
8
|
+
defined in YAML specifications by coordinating wagons through contract-based communication.
|
|
9
|
+
|
|
10
|
+
TRANSFORMATION (SESSION-12):
|
|
11
|
+
- OLD: Trains in e2e/ (test-only), custom orchestration in game.py
|
|
12
|
+
- NEW: Trains in python/trains/ (production), game.py becomes thin Station Master
|
|
13
|
+
|
|
14
|
+
BENEFITS:
|
|
15
|
+
- Single source of truth (YAML defines both production AND tests)
|
|
16
|
+
- Declarative orchestration (change workflows via YAML, not code)
|
|
17
|
+
- Zero drift (tests use production TrainRunner)
|
|
18
|
+
- Operational visibility (train_id in logs = exact journey executed)
|
|
19
|
+
|
|
20
|
+
cross_references:
|
|
21
|
+
composition_hierarchy:
|
|
22
|
+
- file: "refactor.convention.yaml"
|
|
23
|
+
section: "composition_root.hierarchy"
|
|
24
|
+
note: "Trains add new orchestration layer between game.py and wagon.py"
|
|
25
|
+
|
|
26
|
+
boundaries:
|
|
27
|
+
- file: "boundaries.convention.yaml"
|
|
28
|
+
section: "interaction"
|
|
29
|
+
note: "Trains enforce boundaries via contract-based wagon communication"
|
|
30
|
+
|
|
31
|
+
specifications:
|
|
32
|
+
- file: "../../planner/conventions/train.convention.yaml"
|
|
33
|
+
note: "Planner defines train YAML structure and semantics"
|
|
34
|
+
|
|
35
|
+
# ============================================================================
|
|
36
|
+
# COMPOSITION ROOT HIERARCHY (with Trains)
|
|
37
|
+
# ============================================================================
|
|
38
|
+
|
|
39
|
+
composition_hierarchy:
|
|
40
|
+
description: "Trains sit between application (game.py) and wagons (wagon.py)"
|
|
41
|
+
|
|
42
|
+
levels:
|
|
43
|
+
application:
|
|
44
|
+
file: "python/game.py"
|
|
45
|
+
role: "Station Master - thin router"
|
|
46
|
+
responsibility: "Map user actions → train_ids, invoke TrainRunner"
|
|
47
|
+
size: "< 50 lines of routing logic"
|
|
48
|
+
|
|
49
|
+
train:
|
|
50
|
+
file: "python/trains/runner.py"
|
|
51
|
+
role: "Engine - THE orchestration layer"
|
|
52
|
+
responsibility: "Load YAML, orchestrate wagons, pass artifacts"
|
|
53
|
+
location: "python/trains/"
|
|
54
|
+
note: "NEW level introduced in SESSION-12"
|
|
55
|
+
|
|
56
|
+
wagon:
|
|
57
|
+
file: "python/{wagon}/wagon.py"
|
|
58
|
+
role: "Wagon - domain orchestration"
|
|
59
|
+
responsibility: "Expose run_train(inputs, timing) interface"
|
|
60
|
+
|
|
61
|
+
feature:
|
|
62
|
+
file: "python/{wagon}/{feature}/composition.py"
|
|
63
|
+
role: "Feature - layer wiring"
|
|
64
|
+
responsibility: "DI for domain/application/integration"
|
|
65
|
+
|
|
66
|
+
dependency_flow:
|
|
67
|
+
rule: "Dependencies flow downward only"
|
|
68
|
+
chain: "game.py → trains.runner → wagon.py → composition.py → src/"
|
|
69
|
+
forbidden: "NEVER: wagon imports from trains or game"
|
|
70
|
+
|
|
71
|
+
# ============================================================================
|
|
72
|
+
# TRAIN INFRASTRUCTURE
|
|
73
|
+
# ============================================================================
|
|
74
|
+
|
|
75
|
+
train_structure:
|
|
76
|
+
location: "python/trains/"
|
|
77
|
+
|
|
78
|
+
files:
|
|
79
|
+
runner_py:
|
|
80
|
+
file: "python/trains/runner.py"
|
|
81
|
+
class: "TrainRunner"
|
|
82
|
+
composition_root: true
|
|
83
|
+
responsibilities:
|
|
84
|
+
- "Load train YAML from plan/_trains/{train_id}.yaml"
|
|
85
|
+
- "Orchestrate wagons in sequence"
|
|
86
|
+
- "Pass artifacts as cargo between wagons"
|
|
87
|
+
- "Enforce boundaries (no direct wagon imports)"
|
|
88
|
+
- "Capture execution trace"
|
|
89
|
+
|
|
90
|
+
interface: |
|
|
91
|
+
class TrainRunner:
|
|
92
|
+
def __init__(self, train_yaml_path: str)
|
|
93
|
+
def execute(self, inputs: Dict, timing: Dict, capture_trace: bool) -> TrainResult
|
|
94
|
+
def get_train_info(self) -> Dict
|
|
95
|
+
|
|
96
|
+
models_py:
|
|
97
|
+
file: "python/trains/models.py"
|
|
98
|
+
models:
|
|
99
|
+
- "TrainSpec: Parsed train definition"
|
|
100
|
+
- "TrainStep: Single step in sequence"
|
|
101
|
+
- "TrainResult: Execution result + artifacts"
|
|
102
|
+
- "Cargo: Artifacts passed between wagons"
|
|
103
|
+
|
|
104
|
+
# ============================================================================
|
|
105
|
+
# WAGON TRAIN INTERFACE
|
|
106
|
+
# ============================================================================
|
|
107
|
+
|
|
108
|
+
wagon_train_mode:
|
|
109
|
+
description: "Wagons must implement run_train() to participate in trains"
|
|
110
|
+
|
|
111
|
+
signature: "def run_train(inputs: Dict[str, Any], timing: Dict[str, float] = None) -> Dict[str, Any]"
|
|
112
|
+
|
|
113
|
+
location: "python/{wagon}/wagon.py (module-level function or class method)"
|
|
114
|
+
|
|
115
|
+
responsibilities:
|
|
116
|
+
- "Extract relevant artifacts from cargo (inputs dict)"
|
|
117
|
+
- "Execute wagon business logic"
|
|
118
|
+
- "Return artifact dictionary (URN → data)"
|
|
119
|
+
- "Handle timing configuration (delays, timeouts)"
|
|
120
|
+
|
|
121
|
+
example: |
|
|
122
|
+
# python/pace_dilemmas/wagon.py
|
|
123
|
+
def run_train(inputs: Dict[str, Any], timing: Dict[str, float] = None) -> Dict[str, Any]:
|
|
124
|
+
"""Train mode: Execute wagon in train orchestration."""
|
|
125
|
+
# Extract inputs
|
|
126
|
+
fragments = inputs.get("fragments", [])
|
|
127
|
+
domain_scores = inputs.get("domain_scores", {})
|
|
128
|
+
|
|
129
|
+
# Execute logic
|
|
130
|
+
dilemma = pair_fragments_use_case.execute(fragments, domain_scores)
|
|
131
|
+
|
|
132
|
+
# Return artifacts (URN → data)
|
|
133
|
+
return {
|
|
134
|
+
"match:dilemma.selected": {"dilemma_id": dilemma.id, ...},
|
|
135
|
+
"mechanic:quiz.progress": {"answered_count": 1, ...}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
multi_mode_pattern:
|
|
139
|
+
description: "Wagons support multiple execution modes"
|
|
140
|
+
cli: "Interactive demo (python3 wagon.py)"
|
|
141
|
+
http: "API endpoints (via game.py)"
|
|
142
|
+
train: "Production orchestration (via TrainRunner)"
|
|
143
|
+
|
|
144
|
+
# ============================================================================
|
|
145
|
+
# CARGO AND ARTIFACTS
|
|
146
|
+
# ============================================================================
|
|
147
|
+
|
|
148
|
+
cargo_pattern:
|
|
149
|
+
description: "Cargo carries artifacts between wagons during execution"
|
|
150
|
+
|
|
151
|
+
cargo_model:
|
|
152
|
+
structure: "Dict[artifact_urn, artifact_data]"
|
|
153
|
+
operations:
|
|
154
|
+
- "cargo.add_artifact(urn, data)"
|
|
155
|
+
- "cargo.get_artifact(urn)"
|
|
156
|
+
- "cargo.has_artifact(urn)"
|
|
157
|
+
|
|
158
|
+
artifact_flow: |
|
|
159
|
+
Step 1: pace_dilemmas produces match:dilemma.selected
|
|
160
|
+
Cargo: {"match:dilemma.selected": {...}}
|
|
161
|
+
|
|
162
|
+
Step 2: resolve_dilemmas consumes match:dilemma.selected, produces mechanic:decision.choice
|
|
163
|
+
Cargo: {"match:dilemma.selected": {...}, "mechanic:decision.choice": {...}}
|
|
164
|
+
|
|
165
|
+
Step 3: Artifacts accumulate as train progresses
|
|
166
|
+
|
|
167
|
+
contract_validation:
|
|
168
|
+
validator: "e2e/shared/fixtures/contract_validator.py"
|
|
169
|
+
schemas: "contracts/{theme}/{domain}/{resource}.schema.json"
|
|
170
|
+
method: "JSON Schema Draft-07 validation"
|
|
171
|
+
|
|
172
|
+
# ============================================================================
|
|
173
|
+
# STATION MASTER PATTERN (game.py)
|
|
174
|
+
# ============================================================================
|
|
175
|
+
|
|
176
|
+
station_master:
|
|
177
|
+
description: "game.py becomes thin router that delegates to TrainRunner"
|
|
178
|
+
|
|
179
|
+
pattern: |
|
|
180
|
+
# game.py - Station Master
|
|
181
|
+
from trains.runner import TrainRunner
|
|
182
|
+
|
|
183
|
+
JOURNEY_MAP = {
|
|
184
|
+
"start_quiz": "1006-quiz-workflow-adaptive",
|
|
185
|
+
"start_match": "3001-solo-match-complete"
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@app.post("/action/{action_name}")
|
|
189
|
+
def execute_journey(action_name: str, inputs: dict):
|
|
190
|
+
train_id = JOURNEY_MAP[action_name]
|
|
191
|
+
runner = TrainRunner(f"plan/_trains/{train_id}.yaml")
|
|
192
|
+
result = runner.execute(inputs=inputs)
|
|
193
|
+
return {"success": result.success, "artifacts": result.artifacts}
|
|
194
|
+
|
|
195
|
+
responsibilities:
|
|
196
|
+
- "Route user actions to train IDs"
|
|
197
|
+
- "Invoke TrainRunner"
|
|
198
|
+
- "Translate HTTP ↔ artifacts"
|
|
199
|
+
- "Handle errors"
|
|
200
|
+
|
|
201
|
+
anti_pattern: "Business logic in game.py (belongs in trains/wagons)"
|
|
202
|
+
|
|
203
|
+
# ============================================================================
|
|
204
|
+
# TESTING
|
|
205
|
+
# ============================================================================
|
|
206
|
+
|
|
207
|
+
testing_pattern:
|
|
208
|
+
description: "Tests use production TrainRunner - no mocks, zero drift"
|
|
209
|
+
|
|
210
|
+
e2e_tests:
|
|
211
|
+
location: "e2e/mechanic/test_1006_quiz_workflow_adaptive.py"
|
|
212
|
+
|
|
213
|
+
pattern: |
|
|
214
|
+
def test_ac001_all_dilemmas_selected(train_runner, contract_validator):
|
|
215
|
+
# Execute train (production code)
|
|
216
|
+
result = train_runner.execute(
|
|
217
|
+
train_id="1006-quiz-workflow-adaptive",
|
|
218
|
+
inputs={"quiz_config": {...}}
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
# Validate artifacts
|
|
222
|
+
assert result.success
|
|
223
|
+
contract_validator.validate(
|
|
224
|
+
result.artifacts["mechanic:quiz.completed"],
|
|
225
|
+
"contracts/mechanic/quiz/completed.schema.json"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Assert acceptance criteria
|
|
229
|
+
assert result.artifacts["mechanic:quiz.completed"]["answered_count"] == 10
|
|
230
|
+
|
|
231
|
+
fixtures:
|
|
232
|
+
train_runner:
|
|
233
|
+
source: "from trains.runner import TrainRunner (production)"
|
|
234
|
+
location: "e2e/conftest.py"
|
|
235
|
+
|
|
236
|
+
contract_validator:
|
|
237
|
+
source: "e2e/shared/fixtures/contract_validator.py (real JSON schema validator)"
|
|
238
|
+
library: "jsonschema"
|
|
239
|
+
|
|
240
|
+
# ============================================================================
|
|
241
|
+
# BOUNDARIES
|
|
242
|
+
# ============================================================================
|
|
243
|
+
|
|
244
|
+
boundary_enforcement:
|
|
245
|
+
no_direct_imports:
|
|
246
|
+
rule: "Wagons NEVER import from other wagons"
|
|
247
|
+
enforcement: "TrainRunner uses dynamic imports, artifacts only"
|
|
248
|
+
|
|
249
|
+
contract_communication:
|
|
250
|
+
rule: "Wagons communicate exclusively via artifact contracts"
|
|
251
|
+
enforcement: "Contract schemas validate structure"
|
|
252
|
+
|
|
253
|
+
qualified_imports:
|
|
254
|
+
rule: "All imports fully qualified (from {wagon}.{feature}.src.{layer}...)"
|
|
255
|
+
benefit: "Prevents module shadowing"
|
|
256
|
+
|
|
257
|
+
composition_root_exception:
|
|
258
|
+
rule: "Composition roots may manipulate sys.path"
|
|
259
|
+
files: ["python/trains/runner.py", "python/{wagon}/wagon.py"]
|
|
260
|
+
rationale: "Entrypoints (executed, never imported)"
|
|
261
|
+
|
|
262
|
+
# ============================================================================
|
|
263
|
+
# OBSERVABILITY
|
|
264
|
+
# ============================================================================
|
|
265
|
+
|
|
266
|
+
observability:
|
|
267
|
+
execution_trace:
|
|
268
|
+
enabled: "runner.execute(capture_trace=True)"
|
|
269
|
+
structure: "List[{step, wagon, duration_ms, success, error}]"
|
|
270
|
+
|
|
271
|
+
logging:
|
|
272
|
+
include_train_id: "All log entries include train_id"
|
|
273
|
+
correlation: "train_id + timestamp for cross-wagon correlation"
|
|
274
|
+
replay: "Reproduce execution with train_id + inputs"
|
|
275
|
+
|
|
276
|
+
# ============================================================================
|
|
277
|
+
# MIGRATION GUIDE
|
|
278
|
+
# ============================================================================
|
|
279
|
+
|
|
280
|
+
migration:
|
|
281
|
+
step_1: "Identify user journeys in game.py"
|
|
282
|
+
step_2: "Create train YAML (plan/_trains/{train_id}.yaml)"
|
|
283
|
+
step_3: "Add run_train() to participating wagons"
|
|
284
|
+
step_4: "Refactor game.py to Station Master pattern"
|
|
285
|
+
step_5: "Update tests to use TrainRunner"
|
|
286
|
+
|
|
287
|
+
# ============================================================================
|
|
288
|
+
# ANTI-PATTERNS
|
|
289
|
+
# ============================================================================
|
|
290
|
+
|
|
291
|
+
anti_patterns:
|
|
292
|
+
business_logic_in_runner:
|
|
293
|
+
problem: "TrainRunner contains business logic"
|
|
294
|
+
correct: "Business logic in wagons, TrainRunner only orchestrates"
|
|
295
|
+
|
|
296
|
+
wagon_imports_wagon:
|
|
297
|
+
problem: "Wagon A imports from wagon B"
|
|
298
|
+
correct: "Wagons communicate via artifacts in cargo"
|
|
299
|
+
|
|
300
|
+
test_only_trains:
|
|
301
|
+
problem: "Separate trains for tests vs production"
|
|
302
|
+
correct: "Production trains ARE test subjects"
|
|
303
|
+
|
|
304
|
+
mocking_train_runner:
|
|
305
|
+
problem: "Tests mock TrainRunner"
|
|
306
|
+
correct: "Tests use real TrainRunner"
|
|
307
|
+
|
|
308
|
+
# ============================================================================
|
|
309
|
+
# EXAMPLES
|
|
310
|
+
# ============================================================================
|
|
311
|
+
|
|
312
|
+
examples:
|
|
313
|
+
simple:
|
|
314
|
+
train_id: "1001-decision-standard"
|
|
315
|
+
wagons: ["pace-dilemmas", "resolve-dilemmas", "commit-state"]
|
|
316
|
+
|
|
317
|
+
complex:
|
|
318
|
+
train_id: "1006-quiz-workflow-adaptive"
|
|
319
|
+
wagons: ["supply-fragments", "pace-dilemmas", "juggle-domains", "resolve-dilemmas"]
|
|
320
|
+
artifacts:
|
|
321
|
+
- "match:dilemma.selected"
|
|
322
|
+
- "mechanic:decision.choice"
|
|
323
|
+
- "scenario:graph.pagerank"
|
|
324
|
+
- "mechanic:domain.associations"
|
|
325
|
+
- "mechanic:quiz.completed"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
protocol: refactor_verification
|
|
2
|
+
constraint: "ATDD - Refactor MUST preserve GREEN"
|
|
3
|
+
|
|
4
|
+
before_refactor:
|
|
5
|
+
- id: check_initial_state
|
|
6
|
+
steps:
|
|
7
|
+
- run: pytest
|
|
8
|
+
expect: all_pass
|
|
9
|
+
on_fail: "BLOCKED - fix tests before refactoring"
|
|
10
|
+
|
|
11
|
+
- run: git status --porcelain
|
|
12
|
+
expect: clean_or_staged_only
|
|
13
|
+
on_fail: "WARN - uncommitted changes detected"
|
|
14
|
+
|
|
15
|
+
during_refactor:
|
|
16
|
+
per_step:
|
|
17
|
+
- apply_with_journal:
|
|
18
|
+
before: capture_pre_state
|
|
19
|
+
transform: execute_step
|
|
20
|
+
after: capture_diff
|
|
21
|
+
save: journal_entry
|
|
22
|
+
|
|
23
|
+
- verify_step:
|
|
24
|
+
run: pytest
|
|
25
|
+
on_pass: continue_to_next_step
|
|
26
|
+
on_fail:
|
|
27
|
+
- rollback: journal.rollback_step(current_step_id)
|
|
28
|
+
- abort: "Step failed verification"
|
|
29
|
+
|
|
30
|
+
after_refactor:
|
|
31
|
+
- id: final_verification
|
|
32
|
+
steps:
|
|
33
|
+
- run: pytest
|
|
34
|
+
expect: all_pass
|
|
35
|
+
on_fail:
|
|
36
|
+
- rollback: journal.rollback_batch(all_steps)
|
|
37
|
+
- abort: "Final verification failed - all changes rolled back"
|
|
38
|
+
|
|
39
|
+
- report: "Refactor successful - all tests GREEN"
|
|
40
|
+
|
|
41
|
+
rollback:
|
|
42
|
+
method: "Git-based with structured logging"
|
|
43
|
+
implementation: "git restore (runtime helpers archived)"
|
|
44
|
+
how: "git restore --source=HEAD -- <files>"
|
|
45
|
+
logging: "agent.log with trace_id for auditability"
|
|
46
|
+
|
|
47
|
+
on_step_fail:
|
|
48
|
+
- log_refactor_step_end(step_id, "RED", trace_id)
|
|
49
|
+
- rollback_refactor_step(files, trace_id=trace_id)
|
|
50
|
+
|
|
51
|
+
on_final_fail:
|
|
52
|
+
- rollback_refactor_step(all_files, trace_id=trace_id)
|
|
53
|
+
- git checkout - (return to original branch)
|