@@ -174,6 +174,43 @@ def _coerce_selected_flag(series: pd.Series) -> pd.Series:
174174 )
175175
176176
177+ def _selected_symbols_ordered_by_rank (
178+ latest_ranking : pd .DataFrame ,
179+ selected_mask : pd .Series ,
180+ errors : list [str ],
181+ ) -> list [str ]:
182+ selected_rows = latest_ranking .loc [selected_mask , ["symbol" , "current_rank" ]].copy ()
183+ if selected_rows .empty :
184+ return []
185+
186+ selected_rows ["_symbol_normalized" ] = selected_rows ["symbol" ].astype (str ).str .strip ().str .upper ()
187+ selected_rows ["_current_rank_numeric" ] = pd .to_numeric (selected_rows ["current_rank" ], errors = "coerce" )
188+
189+ invalid_rank_symbols = selected_rows .loc [
190+ selected_rows ["_current_rank_numeric" ].isna (),
191+ "_symbol_normalized" ,
192+ ].tolist ()
193+ if invalid_rank_symbols :
194+ errors .append (
195+ "latest_ranking.csv selected rows must have numeric current_rank values: "
196+ + ", " .join (invalid_rank_symbols )
197+ )
198+ return []
199+
200+ duplicated_rank_symbols = selected_rows .loc [
201+ selected_rows ["_current_rank_numeric" ].duplicated (keep = False ),
202+ "_symbol_normalized" ,
203+ ].tolist ()
204+ if duplicated_rank_symbols :
205+ errors .append (
206+ "latest_ranking.csv selected current_rank values must be unique: "
207+ + ", " .join (duplicated_rank_symbols )
208+ )
209+
210+ ordered = selected_rows .sort_values ("_current_rank_numeric" , kind = "mergesort" )
211+ return ordered ["_symbol_normalized" ].tolist ()
212+
213+
177214def validate_release_outputs (
178215 output_dir : Path | str ,
179216 * ,
@@ -399,10 +436,17 @@ def validate_release_outputs(
399436 errors ,
400437 )
401438 )
439+ selected_symbols_by_rank = _selected_symbols_ordered_by_rank (latest_ranking , selected_mask , errors )
402440 if live_pool_symbols and not set (live_pool_symbols ).issubset (set (ranking_symbols )):
403441 errors .append ("live_pool.json symbols must all be present in latest_ranking.csv" )
404442 if live_pool_symbols and not set (live_pool_symbols ).issubset (selected_symbols ):
405443 errors .append ("live_pool.json symbols must all be selected in latest_ranking.csv" )
444+ if live_pool_symbols and selected_symbols_by_rank :
445+ expected_live_pool_symbols = selected_symbols_by_rank [: len (live_pool_symbols )]
446+ if live_pool_symbols != expected_live_pool_symbols :
447+ errors .append (
448+ "live_pool.json symbols must match selected latest_ranking.csv symbols ordered by current_rank"
449+ )
406450
407451 if manifest_present :
408452 manifest_mode = str (manifest .get ("mode" , "" )).strip ()
0 commit comments