@@ -30,9 +30,42 @@ def _write_bundle(self, compat_root: Path, bundle_name: str, repos: dict[str, st
3030 block .append (f'{ repo } = "{ ref } "' )
3131 bundle_path .write_text ("\n " .join (block ) + "\n " , encoding = "utf-8" )
3232
33+ def _write_repo_tiers (self , compat_root : Path ) -> None :
34+ path = compat_root / "compat" / "repo-tiers.toml"
35+ path .parent .mkdir (parents = True , exist_ok = True )
36+ path .write_text (
37+ "[tiers]\n "
38+ 'core = { name = "core" }\n '
39+ 'strategy_lib = { name = "strategy-lib" }\n '
40+ 'pipeline = { name = "pipeline" }\n '
41+ 'runtime = { name = "runtime" }\n '
42+ 'ops = { name = "ops/tooling" }\n '
43+ "\n [upgrade_rings]\n "
44+ 'ring_a = "core"\n '
45+ 'ring_b = "strategy-lib"\n '
46+ 'ring_c = "pipeline"\n '
47+ 'ring_d = "runtime"\n '
48+ 'ring_e = "ops/tooling"\n '
49+ "\n [upgrade_rules]\n "
50+ "allow_drift = [\n "
51+ ' "ops/tooling:runtime",\n '
52+ ' "ops/tooling:pipeline",\n '
53+ ' "ops/tooling:strategy-lib",\n '
54+ ' "ops/tooling:core",\n '
55+ ' "runtime:pipeline",\n '
56+ ' "runtime:strategy-lib",\n '
57+ ' "runtime:core",\n '
58+ ' "pipeline:strategy-lib",\n '
59+ ' "pipeline:core",\n '
60+ ' "strategy-lib:core",\n '
61+ "]\n " ,
62+ encoding = "utf-8" ,
63+ )
64+
3365 def test_root_compat_bundle_and_ring_schema (self ):
3466 with tempfile .TemporaryDirectory () as workspace :
3567 compat_root = Path (workspace )
68+ self ._write_repo_tiers (compat_root )
3669 self ._write_bundle (
3770 compat_root ,
3871 "2026.07.2" ,
@@ -63,6 +96,7 @@ def test_root_compat_bundle_and_ring_schema(self):
6396 def test_not_enforced_bundle_reports_warning_for_short_sha_and_mismatch_but_main_stays_issue (self ):
6497 with tempfile .TemporaryDirectory () as workspace :
6598 compat_root = Path (workspace )
99+ self ._write_repo_tiers (compat_root )
66100 self ._write_bundle (
67101 compat_root ,
68102 "2026.07.2" ,
@@ -103,6 +137,7 @@ def test_not_enforced_bundle_reports_warning_for_short_sha_and_mismatch_but_main
103137 def test_live_constraint_files_allow_full_sha_drift_from_bundle (self ):
104138 with tempfile .TemporaryDirectory () as workspace :
105139 compat_root = Path (workspace )
140+ self ._write_repo_tiers (compat_root )
106141 self ._write_bundle (
107142 compat_root ,
108143 "2026.07.2" ,
@@ -111,7 +146,7 @@ def test_live_constraint_files_allow_full_sha_drift_from_bundle(self):
111146 repo_root = self ._make_repo_root (
112147 qsl_toml = (
113148 'tier = "core"\n '
114- "ring = 0 \n "
149+ 'upgrade_ring = "ring_a" \n '
115150 "allow_legacy = true\n "
116151 "[compat]\n "
117152 'bundle = "2026.07.2"\n '
@@ -135,6 +170,7 @@ def test_live_constraint_files_allow_full_sha_drift_from_bundle(self):
135170 def test_live_constraint_files_still_block_short_refs (self ):
136171 with tempfile .TemporaryDirectory () as workspace :
137172 compat_root = Path (workspace )
173+ self ._write_repo_tiers (compat_root )
138174 self ._write_bundle (
139175 compat_root ,
140176 "2026.07.2" ,
@@ -143,7 +179,7 @@ def test_live_constraint_files_still_block_short_refs(self):
143179 repo_root = self ._make_repo_root (
144180 qsl_toml = (
145181 'tier = "core"\n '
146- "ring = 0 \n "
182+ 'upgrade_ring = "ring_a" \n '
147183 "allow_legacy = true\n "
148184 "[compat]\n "
149185 'bundle = "2026.07.2"\n '
@@ -167,6 +203,7 @@ def test_live_constraint_files_still_block_short_refs(self):
167203 def test_not_enforced_bundle_exception_metadata_notes (self ):
168204 with tempfile .TemporaryDirectory () as workspace :
169205 compat_root = Path (workspace )
206+ self ._write_repo_tiers (compat_root )
170207 self ._write_bundle (
171208 compat_root ,
172209 "2026.07.2" ,
@@ -175,7 +212,7 @@ def test_not_enforced_bundle_exception_metadata_notes(self):
175212 repo_root = self ._make_repo_root (
176213 qsl_toml = (
177214 'tier = "pipeline"\n '
178- "ring = 2 \n "
215+ 'upgrade_ring = "ring_c" \n '
179216 'owner = "pipeline-team"\n '
180217 'expires_at = "2099-12-31"\n '
181218 'next_action = "remove transition pin drift"\n '
@@ -198,6 +235,7 @@ def test_not_enforced_bundle_exception_metadata_notes(self):
198235 def test_legacy_reason_suppresses_allowed_legacy_warning (self ):
199236 with tempfile .TemporaryDirectory () as workspace :
200237 compat_root = Path (workspace )
238+ self ._write_repo_tiers (compat_root )
201239 self ._write_bundle (
202240 compat_root ,
203241 "2026.07.2" ,
@@ -206,7 +244,7 @@ def test_legacy_reason_suppresses_allowed_legacy_warning(self):
206244 repo_root = self ._make_repo_root (
207245 qsl_toml = (
208246 'tier = "pipeline"\n '
209- "ring = 2 \n "
247+ 'upgrade_ring = "ring_c" \n '
210248 "allow_legacy = true\n "
211249 'legacy_reason = "runtime deployment compatibility"\n '
212250 'owner = "runtime-team"\n '
@@ -232,6 +270,104 @@ def test_legacy_reason_suppresses_allowed_legacy_warning(self):
232270 self .assertIn ("bundle pin mismatch" , warnings [0 ])
233271 self .assertIn ("legacy_reason=runtime deployment compatibility" , notes )
234272
273+ def test_non_canonical_tier_and_ring_emit_warnings (self ):
274+ with tempfile .TemporaryDirectory () as workspace :
275+ compat_root = Path (workspace )
276+ self ._write_repo_tiers (compat_root )
277+ self ._write_bundle (
278+ compat_root ,
279+ "2026.07.2" ,
280+ {"QuantPlatformKit" : "37c81901160c5b31127a27dba1c63944933fb6bf" },
281+ )
282+ repo_root = self ._make_repo_root (
283+ qsl_toml = (
284+ 'tier = "strategy-library"\n '
285+ "ring = 1\n "
286+ "[compat]\n "
287+ 'bundle = "2026.07.2"\n '
288+ ),
289+ pyproject = (
290+ 'dependencies = ["quant-platform-kit @ '
291+ 'git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@37c81901160c5b31127a27dba1c63944933fb6bf"]\n '
292+ ),
293+ )
294+
295+ ok , issues , warnings , _notes = check_qsl_compat ._check (repo_root = repo_root , compat_root = compat_root )
296+
297+ self .assertTrue (ok )
298+ self .assertEqual (issues , [])
299+ self .assertTrue (any ("non-canonical qsl.tier 'strategy-library'" in item for item in warnings ))
300+ self .assertTrue (any ("non-canonical qsl.upgrade_ring '1'" in item for item in warnings ))
301+
302+ def test_runtime_platform_alias_maps_to_runtime_without_direction_error (self ):
303+ with tempfile .TemporaryDirectory () as workspace :
304+ root = Path (workspace )
305+ compat_root = root / "QuantRuntimeSettings"
306+ self ._write_repo_tiers (compat_root )
307+ self ._write_bundle (
308+ compat_root ,
309+ "2026.07.2" ,
310+ {"QuantPlatformKit" : "37c81901160c5b31127a27dba1c63944933fb6bf" },
311+ )
312+ (root / "QuantPlatformKit" ).mkdir (parents = True , exist_ok = True )
313+ (root / "QuantPlatformKit" / "qsl.toml" ).write_text (
314+ 'tier = "core"\n upgrade_ring = "ring_a"\n [compat]\n bundle = "2026.07.2"\n ' ,
315+ encoding = "utf-8" ,
316+ )
317+ repo_root = self ._make_repo_root (
318+ qsl_toml = (
319+ 'tier = "runtime-platform"\n '
320+ "ring = 3\n "
321+ "[compat]\n "
322+ 'bundle = "2026.07.2"\n '
323+ ),
324+ pyproject = (
325+ 'dependencies = ["quant-platform-kit @ '
326+ 'git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@37c81901160c5b31127a27dba1c63944933fb6bf"]\n '
327+ ),
328+ )
329+
330+ ok , issues , warnings , _notes = check_qsl_compat ._check (repo_root = repo_root , compat_root = compat_root )
331+
332+ self .assertTrue (ok )
333+ self .assertEqual (issues , [])
334+ self .assertTrue (any ("non-canonical qsl.tier 'runtime-platform'" in item for item in warnings ))
335+ self .assertTrue (any ("non-canonical qsl.upgrade_ring '3'" in item for item in warnings ))
336+
337+ def test_forbidden_dependency_direction_is_reported (self ):
338+ with tempfile .TemporaryDirectory () as workspace :
339+ root = Path (workspace )
340+ compat_root = root / "QuantRuntimeSettings"
341+ self ._write_repo_tiers (compat_root )
342+ self ._write_bundle (
343+ compat_root ,
344+ "2026.07.2" ,
345+ {"UsEquityStrategies" : "17ddb86c72d44b2c7b78ba7a10d8f71b21180166" },
346+ )
347+ (root / "UsEquityStrategies" ).mkdir (parents = True , exist_ok = True )
348+ (root / "UsEquityStrategies" / "qsl.toml" ).write_text (
349+ 'tier = "strategy-lib"\n upgrade_ring = "ring_b"\n [compat]\n bundle = "2026.07.2"\n ' ,
350+ encoding = "utf-8" ,
351+ )
352+ repo_root = self ._make_repo_root (
353+ qsl_toml = (
354+ 'tier = "core"\n '
355+ 'upgrade_ring = "ring_a"\n '
356+ "[compat]\n "
357+ 'bundle = "2026.07.2"\n '
358+ ),
359+ pyproject = (
360+ 'dependencies = ["us-equity-strategies @ '
361+ 'git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@17ddb86c72d44b2c7b78ba7a10d8f71b21180166"]\n '
362+ ),
363+ )
364+
365+ ok , issues , warnings , _notes = check_qsl_compat ._check (repo_root = repo_root , compat_root = compat_root )
366+
367+ self .assertFalse (ok )
368+ self .assertEqual (warnings , [])
369+ self .assertTrue (any ("forbidden dependency direction" in item for item in issues ))
370+
235371
236372if __name__ == "__main__" :
237373 unittest .main ()
0 commit comments