atdd 0.4.7__py3-none-any.whl → 0.6.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.
- atdd/coach/schemas/config.schema.json +63 -0
- atdd/coach/utils/coverage_phase.py +97 -0
- atdd/coach/validators/shared_fixtures.py +154 -0
- atdd/coder/conventions/coverage.convention.yaml +85 -0
- atdd/coder/validators/conftest.py +5 -0
- atdd/coder/validators/test_hierarchy_coverage.py +361 -0
- atdd/planner/conventions/coverage.convention.yaml +95 -0
- atdd/planner/validators/test_hierarchy_coverage.py +433 -0
- atdd/tester/conventions/coverage.convention.yaml +114 -0
- atdd/tester/validators/test_hierarchy_coverage.py +604 -0
- {atdd-0.4.7.dist-info → atdd-0.6.0.dist-info}/METADATA +1 -1
- {atdd-0.4.7.dist-info → atdd-0.6.0.dist-info}/RECORD +16 -8
- {atdd-0.4.7.dist-info → atdd-0.6.0.dist-info}/WHEEL +0 -0
- {atdd-0.4.7.dist-info → atdd-0.6.0.dist-info}/entry_points.txt +0 -0
- {atdd-0.4.7.dist-info → atdd-0.6.0.dist-info}/licenses/LICENSE +0 -0
- {atdd-0.4.7.dist-info → atdd-0.6.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Coder hierarchy coverage validation.
|
|
3
|
+
|
|
4
|
+
ATDD Hierarchy Coverage Spec v0.1 - Section 4: Coder Coverage Rules
|
|
5
|
+
|
|
6
|
+
Validates:
|
|
7
|
+
- Feature <-> Implementation (COVERAGE-CODE-4.1)
|
|
8
|
+
- Implementation <-> Tests (COVERAGE-CODE-4.2)
|
|
9
|
+
|
|
10
|
+
Architecture:
|
|
11
|
+
- Uses shared fixtures from atdd.coach.validators.shared_fixtures
|
|
12
|
+
- Phased rollout via atdd.coach.utils.coverage_phase
|
|
13
|
+
- Exception handling via .atdd/config.yaml coverage.exceptions
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Dict, List, Set, Tuple, Any
|
|
19
|
+
|
|
20
|
+
from atdd.coach.utils.repo import find_repo_root
|
|
21
|
+
from atdd.coach.utils.coverage_phase import (
|
|
22
|
+
CoveragePhase,
|
|
23
|
+
should_enforce,
|
|
24
|
+
emit_coverage_warning
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Path constants
|
|
29
|
+
REPO_ROOT = find_repo_root()
|
|
30
|
+
PLAN_DIR = REPO_ROOT / "plan"
|
|
31
|
+
PYTHON_DIR = REPO_ROOT / "python"
|
|
32
|
+
SUPABASE_DIR = REPO_ROOT / "supabase"
|
|
33
|
+
WEB_DIR = REPO_ROOT / "web"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# ============================================================================
|
|
37
|
+
# HELPER FUNCTIONS
|
|
38
|
+
# ============================================================================
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def find_python_implementations(wagon_slug: str, feature_slug: str) -> List[Path]:
|
|
42
|
+
"""
|
|
43
|
+
Find Python implementation files for a feature.
|
|
44
|
+
|
|
45
|
+
Searches for:
|
|
46
|
+
- python/{wagon}/use_case_{feature}.py
|
|
47
|
+
- python/{wagon}/service_{feature}.py
|
|
48
|
+
- python/{wagon}/{feature}_handler.py
|
|
49
|
+
- python/{wagon}/{feature}.py
|
|
50
|
+
"""
|
|
51
|
+
implementations = []
|
|
52
|
+
|
|
53
|
+
# Convert slugs to filesystem format
|
|
54
|
+
wagon_dir = wagon_slug.replace("-", "_")
|
|
55
|
+
feature_file = feature_slug.replace("-", "_")
|
|
56
|
+
|
|
57
|
+
wagon_path = PYTHON_DIR / wagon_dir
|
|
58
|
+
if not wagon_path.exists():
|
|
59
|
+
return implementations
|
|
60
|
+
|
|
61
|
+
# Check various patterns
|
|
62
|
+
patterns = [
|
|
63
|
+
f"use_case_{feature_file}.py",
|
|
64
|
+
f"service_{feature_file}.py",
|
|
65
|
+
f"{feature_file}_handler.py",
|
|
66
|
+
f"{feature_file}.py",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
for pattern in patterns:
|
|
70
|
+
impl_path = wagon_path / pattern
|
|
71
|
+
if impl_path.exists():
|
|
72
|
+
implementations.append(impl_path)
|
|
73
|
+
|
|
74
|
+
# Also search subdirectories
|
|
75
|
+
for subdir in wagon_path.iterdir():
|
|
76
|
+
if subdir.is_dir() and not subdir.name.startswith("_"):
|
|
77
|
+
for pattern in patterns:
|
|
78
|
+
impl_path = subdir / pattern
|
|
79
|
+
if impl_path.exists():
|
|
80
|
+
implementations.append(impl_path)
|
|
81
|
+
|
|
82
|
+
return implementations
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def find_typescript_implementations(wagon_slug: str, feature_slug: str) -> List[Path]:
|
|
86
|
+
"""
|
|
87
|
+
Find TypeScript implementation files for a feature.
|
|
88
|
+
|
|
89
|
+
Searches for:
|
|
90
|
+
- supabase/functions/{wagon}/{feature}/index.ts
|
|
91
|
+
- supabase/functions/{wagon}/{feature}/handler.ts
|
|
92
|
+
- supabase/functions/{wagon}/{feature}.ts
|
|
93
|
+
"""
|
|
94
|
+
implementations = []
|
|
95
|
+
|
|
96
|
+
functions_dir = SUPABASE_DIR / "functions"
|
|
97
|
+
if not functions_dir.exists():
|
|
98
|
+
return implementations
|
|
99
|
+
|
|
100
|
+
# Check various structures
|
|
101
|
+
# Pattern 1: supabase/functions/{wagon}/{feature}/
|
|
102
|
+
feature_dir = functions_dir / wagon_slug / feature_slug
|
|
103
|
+
if feature_dir.exists():
|
|
104
|
+
for pattern in ["index.ts", "handler.ts"]:
|
|
105
|
+
impl_path = feature_dir / pattern
|
|
106
|
+
if impl_path.exists():
|
|
107
|
+
implementations.append(impl_path)
|
|
108
|
+
|
|
109
|
+
# Pattern 2: supabase/functions/{wagon}/{feature}.ts
|
|
110
|
+
wagon_dir = functions_dir / wagon_slug
|
|
111
|
+
if wagon_dir.exists():
|
|
112
|
+
feature_file = wagon_dir / f"{feature_slug}.ts"
|
|
113
|
+
if feature_file.exists():
|
|
114
|
+
implementations.append(feature_file)
|
|
115
|
+
|
|
116
|
+
return implementations
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def find_web_implementations(wagon_slug: str, feature_slug: str) -> List[Path]:
|
|
120
|
+
"""
|
|
121
|
+
Find web/frontend implementation files for a feature.
|
|
122
|
+
|
|
123
|
+
Searches for:
|
|
124
|
+
- web/src/features/{wagon}/{feature}/
|
|
125
|
+
- web/src/components/{feature}/
|
|
126
|
+
"""
|
|
127
|
+
implementations = []
|
|
128
|
+
|
|
129
|
+
# Pattern 1: web/src/features/{wagon}/{feature}/
|
|
130
|
+
features_dir = WEB_DIR / "src" / "features" / wagon_slug / feature_slug
|
|
131
|
+
if features_dir.exists():
|
|
132
|
+
for pattern in ["index.tsx", "index.ts", f"{feature_slug}.tsx"]:
|
|
133
|
+
impl_path = features_dir / pattern
|
|
134
|
+
if impl_path.exists():
|
|
135
|
+
implementations.append(impl_path)
|
|
136
|
+
|
|
137
|
+
# Pattern 2: web/src/components/{feature}/
|
|
138
|
+
components_dir = WEB_DIR / "src" / "components" / feature_slug
|
|
139
|
+
if components_dir.exists():
|
|
140
|
+
implementations.append(components_dir)
|
|
141
|
+
|
|
142
|
+
return implementations
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def has_implementation(wagon_slug: str, feature_slug: str) -> bool:
|
|
146
|
+
"""
|
|
147
|
+
Check if a feature has any implementation.
|
|
148
|
+
"""
|
|
149
|
+
python_impls = find_python_implementations(wagon_slug, feature_slug)
|
|
150
|
+
ts_impls = find_typescript_implementations(wagon_slug, feature_slug)
|
|
151
|
+
web_impls = find_web_implementations(wagon_slug, feature_slug)
|
|
152
|
+
|
|
153
|
+
return len(python_impls) > 0 or len(ts_impls) > 0 or len(web_impls) > 0
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def find_tests_for_implementation(impl_path: Path) -> List[Path]:
|
|
157
|
+
"""
|
|
158
|
+
Find test files that might test an implementation.
|
|
159
|
+
"""
|
|
160
|
+
tests = []
|
|
161
|
+
|
|
162
|
+
if not impl_path.exists():
|
|
163
|
+
return tests
|
|
164
|
+
|
|
165
|
+
# For Python implementations
|
|
166
|
+
if impl_path.suffix == ".py":
|
|
167
|
+
impl_dir = impl_path.parent
|
|
168
|
+
impl_name = impl_path.stem
|
|
169
|
+
|
|
170
|
+
# Look for test_*.py in same directory
|
|
171
|
+
for test_file in impl_dir.glob("test_*.py"):
|
|
172
|
+
if impl_name in test_file.stem:
|
|
173
|
+
tests.append(test_file)
|
|
174
|
+
|
|
175
|
+
# Look for test file with matching name
|
|
176
|
+
test_file = impl_dir / f"test_{impl_name}.py"
|
|
177
|
+
if test_file.exists() and test_file not in tests:
|
|
178
|
+
tests.append(test_file)
|
|
179
|
+
|
|
180
|
+
# For TypeScript implementations
|
|
181
|
+
elif impl_path.suffix == ".ts":
|
|
182
|
+
impl_dir = impl_path.parent
|
|
183
|
+
|
|
184
|
+
# Look for *.test.ts in same directory or test/ subdirectory
|
|
185
|
+
for test_file in impl_dir.glob("*.test.ts"):
|
|
186
|
+
tests.append(test_file)
|
|
187
|
+
|
|
188
|
+
test_dir = impl_dir / "test"
|
|
189
|
+
if test_dir.exists():
|
|
190
|
+
for test_file in test_dir.glob("*.test.ts"):
|
|
191
|
+
tests.append(test_file)
|
|
192
|
+
|
|
193
|
+
return tests
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# ============================================================================
|
|
197
|
+
# COVERAGE-CODE-4.1: Feature <-> Implementation Coverage
|
|
198
|
+
# ============================================================================
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@pytest.mark.coder
|
|
202
|
+
def test_all_features_have_implementations(feature_files, coverage_exceptions):
|
|
203
|
+
"""
|
|
204
|
+
COVERAGE-CODE-4.1: Every feature has implementation code.
|
|
205
|
+
|
|
206
|
+
Given: Feature files in plan/*/features/
|
|
207
|
+
When: Searching for corresponding implementation files
|
|
208
|
+
Then: Every feature has at least one implementation in python/, supabase/, or web/
|
|
209
|
+
"""
|
|
210
|
+
allowed_features = set(coverage_exceptions.get("features_without_implementation", []))
|
|
211
|
+
violations = []
|
|
212
|
+
|
|
213
|
+
for path, feature_data in feature_files:
|
|
214
|
+
# Get wagon slug from path
|
|
215
|
+
wagon_dir = path.parent.parent.name # plan/{wagon}/features/{feature}.yaml
|
|
216
|
+
wagon_slug = wagon_dir.replace("_", "-")
|
|
217
|
+
|
|
218
|
+
# Get feature slug
|
|
219
|
+
feature_slug = path.stem.replace("_", "-")
|
|
220
|
+
feature_urn = feature_data.get("urn", f"feature:{wagon_slug}:{feature_slug}")
|
|
221
|
+
|
|
222
|
+
# Skip draft features
|
|
223
|
+
status = feature_data.get("status", "")
|
|
224
|
+
if status == "draft":
|
|
225
|
+
continue
|
|
226
|
+
|
|
227
|
+
# Skip allowed exceptions
|
|
228
|
+
if feature_urn in allowed_features or feature_slug in allowed_features:
|
|
229
|
+
continue
|
|
230
|
+
|
|
231
|
+
# Check for implementations
|
|
232
|
+
if not has_implementation(wagon_slug, feature_slug):
|
|
233
|
+
violations.append(
|
|
234
|
+
f"{feature_urn}: no implementation found in python/, supabase/, or web/"
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
if violations:
|
|
238
|
+
if should_enforce(CoveragePhase.FULL_ENFORCEMENT):
|
|
239
|
+
pytest.fail(
|
|
240
|
+
f"COVERAGE-CODE-4.1: Features without implementations:\n " +
|
|
241
|
+
"\n ".join(violations[:20]) +
|
|
242
|
+
(f"\n ... and {len(violations) - 20} more" if len(violations) > 20 else "") +
|
|
243
|
+
"\n\nImplement feature or add to coverage.exceptions.features_without_implementation"
|
|
244
|
+
)
|
|
245
|
+
else:
|
|
246
|
+
for violation in violations[:10]:
|
|
247
|
+
emit_coverage_warning(
|
|
248
|
+
"COVERAGE-CODE-4.1",
|
|
249
|
+
violation,
|
|
250
|
+
CoveragePhase.FULL_ENFORCEMENT
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# ============================================================================
|
|
255
|
+
# COVERAGE-CODE-4.2: Implementation <-> Tests Coverage
|
|
256
|
+
# ============================================================================
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@pytest.mark.coder
|
|
260
|
+
def test_all_implementations_have_tests(feature_files):
|
|
261
|
+
"""
|
|
262
|
+
COVERAGE-CODE-4.2: Every implementation has at least one test.
|
|
263
|
+
|
|
264
|
+
Given: Feature implementations in python/, supabase/, web/
|
|
265
|
+
When: Searching for corresponding test files
|
|
266
|
+
Then: Every implementation has at least one test file
|
|
267
|
+
"""
|
|
268
|
+
violations = []
|
|
269
|
+
|
|
270
|
+
for path, feature_data in feature_files:
|
|
271
|
+
# Get wagon and feature slugs
|
|
272
|
+
wagon_dir = path.parent.parent.name
|
|
273
|
+
wagon_slug = wagon_dir.replace("_", "-")
|
|
274
|
+
feature_slug = path.stem.replace("_", "-")
|
|
275
|
+
|
|
276
|
+
# Skip draft features
|
|
277
|
+
status = feature_data.get("status", "")
|
|
278
|
+
if status == "draft":
|
|
279
|
+
continue
|
|
280
|
+
|
|
281
|
+
# Find all implementations for this feature
|
|
282
|
+
all_impls = (
|
|
283
|
+
find_python_implementations(wagon_slug, feature_slug) +
|
|
284
|
+
find_typescript_implementations(wagon_slug, feature_slug)
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
for impl_path in all_impls:
|
|
288
|
+
tests = find_tests_for_implementation(impl_path)
|
|
289
|
+
if not tests:
|
|
290
|
+
violations.append(
|
|
291
|
+
f"{impl_path.relative_to(REPO_ROOT)}: no tests found"
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
if violations:
|
|
295
|
+
if should_enforce(CoveragePhase.FULL_ENFORCEMENT):
|
|
296
|
+
pytest.fail(
|
|
297
|
+
f"COVERAGE-CODE-4.2: Implementations without tests:\n " +
|
|
298
|
+
"\n ".join(violations[:20]) +
|
|
299
|
+
(f"\n ... and {len(violations) - 20} more" if len(violations) > 20 else "") +
|
|
300
|
+
"\n\nAdd tests for the implementation"
|
|
301
|
+
)
|
|
302
|
+
else:
|
|
303
|
+
for violation in violations[:10]:
|
|
304
|
+
emit_coverage_warning(
|
|
305
|
+
"COVERAGE-CODE-4.2",
|
|
306
|
+
violation,
|
|
307
|
+
CoveragePhase.FULL_ENFORCEMENT
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
# ============================================================================
|
|
312
|
+
# COVERAGE SUMMARY
|
|
313
|
+
# ============================================================================
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@pytest.mark.coder
|
|
317
|
+
def test_coder_coverage_summary(feature_files):
|
|
318
|
+
"""
|
|
319
|
+
COVERAGE-CODE-SUMMARY: Report coder coverage statistics.
|
|
320
|
+
|
|
321
|
+
This test always passes but reports coverage metrics for visibility.
|
|
322
|
+
"""
|
|
323
|
+
total_features = len(feature_files)
|
|
324
|
+
features_with_impl = 0
|
|
325
|
+
total_implementations = 0
|
|
326
|
+
implementations_with_tests = 0
|
|
327
|
+
|
|
328
|
+
for path, feature_data in feature_files:
|
|
329
|
+
wagon_dir = path.parent.parent.name
|
|
330
|
+
wagon_slug = wagon_dir.replace("_", "-")
|
|
331
|
+
feature_slug = path.stem.replace("_", "-")
|
|
332
|
+
|
|
333
|
+
# Count implementations
|
|
334
|
+
python_impls = find_python_implementations(wagon_slug, feature_slug)
|
|
335
|
+
ts_impls = find_typescript_implementations(wagon_slug, feature_slug)
|
|
336
|
+
web_impls = find_web_implementations(wagon_slug, feature_slug)
|
|
337
|
+
|
|
338
|
+
all_impls = python_impls + ts_impls + web_impls
|
|
339
|
+
if all_impls:
|
|
340
|
+
features_with_impl += 1
|
|
341
|
+
total_implementations += len(all_impls)
|
|
342
|
+
|
|
343
|
+
# Count implementations with tests
|
|
344
|
+
for impl_path in python_impls + ts_impls:
|
|
345
|
+
if find_tests_for_implementation(impl_path):
|
|
346
|
+
implementations_with_tests += 1
|
|
347
|
+
|
|
348
|
+
# Calculate percentages
|
|
349
|
+
feature_impl_pct = (features_with_impl / total_features * 100) if total_features > 0 else 0
|
|
350
|
+
impl_test_pct = (implementations_with_tests / total_implementations * 100) if total_implementations > 0 else 0
|
|
351
|
+
|
|
352
|
+
# Report summary
|
|
353
|
+
summary = (
|
|
354
|
+
f"\n\nCoder Coverage Summary:\n"
|
|
355
|
+
f" Features with implementations: {features_with_impl}/{total_features} ({feature_impl_pct:.1f}%)\n"
|
|
356
|
+
f" Total implementations: {total_implementations}\n"
|
|
357
|
+
f" Implementations with tests: {implementations_with_tests}/{total_implementations} ({impl_test_pct:.1f}%)"
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
# This test always passes - it's informational
|
|
361
|
+
assert True, summary
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
version: "1.0"
|
|
2
|
+
name: "Planner Coverage Convention"
|
|
3
|
+
description: "Section 2 of ATDD Hierarchy Coverage Spec v0.1 - Ensures every planner artifact participates in lifecycle graph"
|
|
4
|
+
|
|
5
|
+
rules:
|
|
6
|
+
- id: "COVERAGE-PLAN-2.1"
|
|
7
|
+
name: "Train - Wagon Coverage"
|
|
8
|
+
description: "Bidirectional coverage between trains and wagons"
|
|
9
|
+
bidirectional:
|
|
10
|
+
- direction: "wagon -> train"
|
|
11
|
+
requirement: "Every wagon must appear in at least one train's participants"
|
|
12
|
+
exceptions: "wagons_not_in_train allow-list or status:draft"
|
|
13
|
+
validator: "test_all_wagons_in_at_least_one_train"
|
|
14
|
+
|
|
15
|
+
- direction: "train -> wagon"
|
|
16
|
+
requirement: "Every wagon: participant must have a wagon manifest"
|
|
17
|
+
validator: "test_all_train_wagon_refs_exist"
|
|
18
|
+
|
|
19
|
+
- id: "COVERAGE-PLAN-2.2"
|
|
20
|
+
name: "Wagon - Feature Coverage"
|
|
21
|
+
description: "Bidirectional coverage between wagons and features"
|
|
22
|
+
bidirectional:
|
|
23
|
+
- direction: "feature -> wagon"
|
|
24
|
+
requirement: "Every feature file must be referenced by its wagon manifest"
|
|
25
|
+
exceptions: "features_orphaned allow-list"
|
|
26
|
+
validator: "test_all_features_in_wagon_manifest"
|
|
27
|
+
|
|
28
|
+
- direction: "wagon -> feature"
|
|
29
|
+
requirement: "Every features[] entry must have corresponding YAML file"
|
|
30
|
+
validator: "test_all_wagon_feature_refs_exist"
|
|
31
|
+
|
|
32
|
+
- id: "COVERAGE-PLAN-2.3"
|
|
33
|
+
name: "WMBT - Feature Coverage"
|
|
34
|
+
description: "Every WMBT must be referenced by at least one feature"
|
|
35
|
+
bidirectional:
|
|
36
|
+
- direction: "wmbt -> feature"
|
|
37
|
+
requirement: "Every WMBT file must appear in at least one feature's wmbts list"
|
|
38
|
+
validator: "test_all_wmbts_in_at_least_one_feature"
|
|
39
|
+
|
|
40
|
+
- id: "COVERAGE-PLAN-2.4"
|
|
41
|
+
name: "WMBT - Acceptance Coverage"
|
|
42
|
+
description: "Every WMBT must have at least one acceptance criterion"
|
|
43
|
+
requirement: "Every WMBT must have at least one acceptance (except status:draft)"
|
|
44
|
+
exceptions: "wmbts_without_acceptance allow-list"
|
|
45
|
+
validator: "test_all_wmbts_have_acceptances"
|
|
46
|
+
|
|
47
|
+
coverage_graph:
|
|
48
|
+
description: "The planner coverage graph ensures traceability from trains down to acceptances"
|
|
49
|
+
levels:
|
|
50
|
+
- name: "Train"
|
|
51
|
+
contains: "Wagons (via participants)"
|
|
52
|
+
validators: ["COVERAGE-PLAN-2.1"]
|
|
53
|
+
|
|
54
|
+
- name: "Wagon"
|
|
55
|
+
contains: "Features (via features[])"
|
|
56
|
+
validators: ["COVERAGE-PLAN-2.1", "COVERAGE-PLAN-2.2"]
|
|
57
|
+
|
|
58
|
+
- name: "Feature"
|
|
59
|
+
contains: "WMBTs (via wmbts[])"
|
|
60
|
+
validators: ["COVERAGE-PLAN-2.2", "COVERAGE-PLAN-2.3"]
|
|
61
|
+
|
|
62
|
+
- name: "WMBT"
|
|
63
|
+
contains: "Acceptances (via acceptances[])"
|
|
64
|
+
validators: ["COVERAGE-PLAN-2.3", "COVERAGE-PLAN-2.4"]
|
|
65
|
+
|
|
66
|
+
- name: "Acceptance"
|
|
67
|
+
contains: "Test criteria"
|
|
68
|
+
validators: ["COVERAGE-PLAN-2.4"]
|
|
69
|
+
|
|
70
|
+
exception_handling:
|
|
71
|
+
description: "How exceptions are handled in coverage validation"
|
|
72
|
+
allow_lists:
|
|
73
|
+
wagons_not_in_train:
|
|
74
|
+
description: "Wagon slugs that are allowed to exist without train coverage"
|
|
75
|
+
use_case: "Utility wagons, shared infrastructure, or wagons under development"
|
|
76
|
+
config_path: "coverage.exceptions.wagons_not_in_train"
|
|
77
|
+
|
|
78
|
+
features_orphaned:
|
|
79
|
+
description: "Feature URNs that are allowed without wagon manifest reference"
|
|
80
|
+
use_case: "Deprecated features being phased out, or experimental features"
|
|
81
|
+
config_path: "coverage.exceptions.features_orphaned"
|
|
82
|
+
|
|
83
|
+
wmbts_without_acceptance:
|
|
84
|
+
description: "WMBT URNs that are allowed without acceptance criteria"
|
|
85
|
+
use_case: "Draft WMBTs or placeholder definitions"
|
|
86
|
+
config_path: "coverage.exceptions.wmbts_without_acceptance"
|
|
87
|
+
|
|
88
|
+
status_exemptions:
|
|
89
|
+
- status: "draft"
|
|
90
|
+
exempted_from: ["COVERAGE-PLAN-2.1", "COVERAGE-PLAN-2.4"]
|
|
91
|
+
reason: "Draft artifacts are work-in-progress and not yet ready for full coverage"
|
|
92
|
+
|
|
93
|
+
rollout:
|
|
94
|
+
phase: "PLANNER_TESTER_ENFORCEMENT"
|
|
95
|
+
description: "Section 2 validators become strict in Phase 2"
|