@@ -1240,3 +1240,73 @@ def check_architectural_focus(output: str, context: dict) -> dict:
12401240 'score' : score ,
12411241 'reason' : ' ' .join (reasons )
12421242 }
1243+
1244+
1245+ def check_completeness (output : str , context : dict ) -> dict :
1246+ """
1247+ Check if specification has comprehensive coverage of requirements.
1248+ Used for complex features like e-commerce checkout.
1249+
1250+ Args:
1251+ output: The generated specification text
1252+ context: Additional context with vars (user_input)
1253+
1254+ Returns:
1255+ dict with 'pass', 'score', and 'reason' keys
1256+ """
1257+ import re
1258+
1259+ output_lower = output .lower ()
1260+ scores = []
1261+ details = []
1262+
1263+ # 1. Check for functional requirements section with numbered items
1264+ fr_pattern = re .compile (r'fr-\d+|functional requirement' , re .IGNORECASE )
1265+ has_functional_reqs = bool (fr_pattern .search (output ))
1266+ if has_functional_reqs :
1267+ scores .append (1.0 )
1268+ details .append ('functional requirements present' )
1269+ else :
1270+ scores .append (0.0 )
1271+ details .append ('missing functional requirements' )
1272+
1273+ # 2. Check for user stories (at least 3)
1274+ user_story_pattern = re .compile (r'as a .+?, i want' , re .IGNORECASE )
1275+ user_stories = user_story_pattern .findall (output )
1276+ story_score = min (1.0 , len (user_stories ) / 3 ) # Full score at 3+ stories
1277+ scores .append (story_score )
1278+ details .append (f'{ len (user_stories )} user stories' )
1279+
1280+ # 3. Check for non-functional requirements
1281+ nfr_terms = ['performance' , 'security' , 'scalability' , 'availability' , 'nfr-' ]
1282+ nfr_found = sum (1 for term in nfr_terms if term in output_lower )
1283+ nfr_score = min (1.0 , nfr_found / 2 ) # Full score at 2+ NFR topics
1284+ scores .append (nfr_score )
1285+ details .append (f'{ nfr_found } NFR topics' )
1286+
1287+ # 4. Check for edge cases section
1288+ edge_case_terms = ['edge case' , 'error' , 'failure' , 'timeout' , 'invalid' , 'exception' ]
1289+ edge_found = sum (1 for term in edge_case_terms if term in output_lower )
1290+ edge_score = min (1.0 , edge_found / 2 ) # Full score at 2+ edge case terms
1291+ scores .append (edge_score )
1292+ details .append (f'{ edge_found } edge case terms' )
1293+
1294+ # 5. Check for specific domain terms based on user input
1295+ user_input = context .get ('vars' , {}).get ('user_input' , '' ).lower ()
1296+
1297+ # For e-commerce checkout, check specific terms
1298+ if 'checkout' in user_input or 'cart' in user_input or 'payment' in user_input :
1299+ ecommerce_terms = ['cart' , 'payment' , 'order' , 'checkout' , 'confirmation' , 'inventory' ]
1300+ ecommerce_found = sum (1 for term in ecommerce_terms if term in output_lower )
1301+ domain_score = min (1.0 , ecommerce_found / 3 ) # Full score at 3+ domain terms
1302+ scores .append (domain_score )
1303+ details .append (f'{ ecommerce_found } /6 e-commerce terms' )
1304+
1305+ # Calculate average score
1306+ avg_score = sum (scores ) / len (scores ) if scores else 0.0
1307+
1308+ return {
1309+ 'pass' : avg_score >= 0.6 ,
1310+ 'score' : avg_score ,
1311+ 'reason' : f'Completeness: { avg_score :.0%} ({ ", " .join (details )} )'
1312+ }
0 commit comments