@@ -428,22 +428,24 @@ def item_identity(item: dict) -> str:
428428
429429def load_state (state_file : Path ) -> dict :
430430 if not state_file .exists ():
431- return {"visible " : {}, "pending" : [] }
431+ return {"retries " : {}}
432432
433433 raw = state_file .read_text ().strip ()
434434 if not raw :
435- return {"visible " : {}, "pending" : [] }
435+ return {"retries " : {}}
436436
437437 data = json .loads (raw )
438438 if not isinstance (data , dict ):
439439 raise ValueError (f"State file must contain a JSON object: { state_file } " )
440440
441- visible = data .get ("visible" , {})
442- pending = data .get ("pending" , [])
443- if not isinstance (visible , dict ) or not isinstance (pending , list ):
441+ retries = data .get ("retries" , {})
442+ if not isinstance (retries , dict ):
444443 raise ValueError (f"Invalid poll state format: { state_file } " )
445444
446- return {"visible" : visible , "pending" : [str (item_id ) for item_id in pending ]}
445+ normalized_retries : dict [str , int ] = {}
446+ for item_id , count in retries .items ():
447+ normalized_retries [str (item_id )] = int (count )
448+ return {"retries" : normalized_retries }
447449
448450
449451def save_state (state_file : Path , state : dict ) -> None :
@@ -963,22 +965,36 @@ def select_entry_from_entries(
963965 state_file : Path ,
964966 target_number : int | None = None ,
965967) -> dict | None :
966- state = load_state (state_file )
967- previous_visible = state ["visible" ]
968-
969- pending = [item_id for item_id in state ["pending" ] if item_id in current_visible ]
970- entered = sorted (
971- (item_id for item_id in current_visible if item_id not in previous_visible ),
972- key = lambda item_id : (current_visible [item_id ]["number" ], item_id ),
968+ del state_file
969+ return select_current_entry_from_entries (
970+ "review" ,
971+ current_visible ,
972+ target_number = target_number ,
973973 )
974- for item_id in entered :
975- if item_id not in pending :
976- pending .append (item_id )
977974
978- state ["visible" ] = current_visible
979- state ["pending" ] = pending
980- save_state (state_file , state )
981975
976+ def current_entry_sort_key (
977+ mode : str ,
978+ entry : dict ,
979+ item_id : str ,
980+ ) -> tuple [int , int , str ] | tuple [int , str ]:
981+ if mode == "ready" :
982+ title = entry .get ("title" ) or ""
983+ if title .startswith ("[Model]" ):
984+ kind_priority = 0
985+ elif title .startswith ("[Rule]" ):
986+ kind_priority = 1
987+ else :
988+ kind_priority = 2
989+ return (kind_priority , int (entry ["number" ]), item_id )
990+ return (int (entry ["number" ]), item_id )
991+
992+
993+ def select_current_entry_from_entries (
994+ mode : str ,
995+ current_visible : dict [str , dict ],
996+ target_number : int | None = None ,
997+ ) -> dict | None :
982998 if target_number is not None :
983999 matching_item_id = next (
9841000 (
@@ -990,17 +1006,40 @@ def select_entry_from_entries(
9901006 )
9911007 if matching_item_id is None :
9921008 return None
993- entry = dict (current_visible [matching_item_id ])
994- entry ["item_id" ] = matching_item_id
995- return entry
1009+ return {
1010+ ** current_visible [matching_item_id ],
1011+ "item_id" : matching_item_id ,
1012+ }
9961013
997- if not pending :
1014+ if not current_visible :
9981015 return None
9991016
1000- item_id = pending [0 ]
1001- entry = dict (current_visible [item_id ])
1002- entry ["item_id" ] = item_id
1003- return entry
1017+ item_id = min (
1018+ current_visible ,
1019+ key = lambda candidate_id : current_entry_sort_key (
1020+ mode ,
1021+ current_visible [candidate_id ],
1022+ candidate_id ,
1023+ ),
1024+ )
1025+ return {
1026+ ** current_visible [item_id ],
1027+ "item_id" : item_id ,
1028+ }
1029+
1030+
1031+ def select_polled_entry_from_entries (
1032+ mode : str ,
1033+ current_visible : dict [str , dict ],
1034+ state_file : Path ,
1035+ target_number : int | None = None ,
1036+ ) -> dict | None :
1037+ del state_file
1038+ return select_current_entry_from_entries (
1039+ mode ,
1040+ current_visible ,
1041+ target_number = target_number ,
1042+ )
10041043
10051044
10061045def select_next_entry (
@@ -1024,7 +1063,8 @@ def select_next_entry(
10241063 batch_pr_fetcher = batch_pr_fetcher ,
10251064 repo_root = repo_root ,
10261065 )
1027- return select_entry_from_entries (
1066+ return select_polled_entry_from_entries (
1067+ mode ,
10281068 current_visible ,
10291069 state_file ,
10301070 target_number = target_number ,
@@ -1033,9 +1073,9 @@ def select_next_entry(
10331073
10341074def ack_item (state_file : Path , item_id : str ) -> None :
10351075 state = load_state (state_file )
1036- state [ "pending" ] = [
1037- pending_id for pending_id in state [ "pending" ] if pending_id != item_id
1038- ]
1076+ retries = state . get ( "retries" , {})
1077+ retries . pop ( str ( item_id ), None )
1078+ state [ "retries" ] = retries
10391079 save_state (state_file , state )
10401080
10411081
@@ -1219,7 +1259,8 @@ def claim_entry_from_entries(
12191259 target_number : int | None = None ,
12201260 mover : Callable [[str , str ], None ] | None = None ,
12211261) -> dict | None :
1222- next_entry = select_entry_from_entries (
1262+ next_entry = select_polled_entry_from_entries (
1263+ mode ,
12231264 current_visible ,
12241265 state_file ,
12251266 target_number = target_number ,
@@ -1650,7 +1691,8 @@ def main(argv: list[str] | None = None) -> int:
16501691 batch_pr_fetcher = batch_fetch_prs_with_reviews ,
16511692 )
16521693 )
1653- next_item = select_entry_from_entries (
1694+ next_item = select_polled_entry_from_entries (
1695+ args .mode ,
16541696 review_entries_map ,
16551697 args .state_file ,
16561698 target_number = args .number ,
0 commit comments