@@ -207,3 +207,43 @@ def check_extra_options(
207207 extra_options_str = ", " .join (f"`{ option } `" for option in extra_options )
208208 msg = f"has these extra options: { extra_options_str } ."
209209 log .warning_for_need (need , msg )
210+
211+
212+ def parse_milestone (value : str ) -> tuple [int , int , int ]:
213+ """Parse a string like 'v0.5' or 'v1.0.0'. No suffixes."""
214+ match = re .match (r"v(\d+)(\.(\d+))?(\.(\d+))?$" , value )
215+ if not match :
216+ raise ValueError (f"Invalid milestone format: { value } " )
217+ major = int (match .group (1 ))
218+ minor = int (match .group (3 ) or 0 )
219+ patch = int (match .group (5 ) or 0 )
220+ return (major , minor , patch )
221+
222+
223+ # req-Id: tool_req__docs_req_attr_validity_consistency
224+ @local_check
225+ def check_validity_consistency (
226+ app : Sphinx ,
227+ need : NeedsInfoType ,
228+ log : CheckLogger ,
229+ ):
230+ """
231+ Check if the attributes valid_from < valid_until.
232+ """
233+ if need ["type" ] not in ("stkh_req" , "feat_req" ):
234+ return
235+
236+ valid_from = need .get ("valid_from" , None )
237+ valid_until = need .get ("valid_until" , None )
238+
239+ if not valid_from or not valid_until :
240+ return
241+
242+ valid_from_version = parse_milestone (valid_from )
243+ valid_until_version = parse_milestone (valid_until )
244+ if valid_from_version >= valid_until_version :
245+ msg = (
246+ "inconsistent validity: "
247+ f"valid_from ({ valid_from } ) >= valid_until ({ valid_until } )."
248+ )
249+ log .warning_for_need (need , msg )
0 commit comments