#!/usr/bin/env python3 """ Validation script for automation tracking issues. This script validates that automation tracking issues follow the standardized format defined in docs/development/automation-tracking.md. Usage: python scripts/validate_automation_tracking.py --repo owner/repo python scripts/validate_automation_tracking.py \ --title "[AUTO-SESSION] Checkpoint (Cycle 15)" python scripts/validate_automation_tracking.py --validate-all """ import argparse import re import sys from typing import Any # Standard agent prefixes and their expected types AGENT_PREFIXES: dict[str, list[str]] = { "SESSION": ["Checkpoint"], "IMP-POOL": ["Health Report", "Status Update"], "WATCHDOG": ["System Health", "Alert"], "GROOMER": ["Grooming Report", "Scope Alert"], "LIAISON": ["Status Update", "Human Activity Summary"], "DOCS": ["Documentation Report"], } # Title format patterns TRACKING_TITLE_PATTERN = re.compile(r"^\[AUTO-([A-Z-]+)\] (.+) \(Cycle (\d+)\)$") ANNOUNCEMENT_TITLE_PATTERN = re.compile(r"^\[AUTO-([A-Z-]+)\] Announce: (.+)$") def validate_tracking_title(title: str) -> tuple[bool, str]: """Validate a tracking issue title format. Returns: (is_valid, error_message) """ # Check for announcement format announce_match = ANNOUNCEMENT_TITLE_PATTERN.match(title) if announce_match: prefix, message = announce_match.groups() if prefix not in AGENT_PREFIXES: known = [f"AUTO-{p}" for p in AGENT_PREFIXES] return ( False, f"Unknown agent prefix 'AUTO-{prefix}'. Known prefixes: {known}", ) if not message.strip(): return False, "Announcement message cannot be empty" return True, "Valid announcement title" # Check for tracking format track_match = TRACKING_TITLE_PATTERN.match(title) if track_match: prefix, issue_type, cycle_str = track_match.groups() # Validate prefix if prefix not in AGENT_PREFIXES: known = [f"AUTO-{p}" for p in AGENT_PREFIXES] return ( False, f"Unknown agent prefix 'AUTO-{prefix}'. Known prefixes: {known}", ) # Validate type valid_types = AGENT_PREFIXES[prefix] if issue_type not in valid_types: return ( False, f"Invalid type '{issue_type}' for prefix " f"'AUTO-{prefix}'. Valid types: {valid_types}", ) # Validate cycle number try: cycle_num = int(cycle_str) if cycle_num < 1: return False, "Cycle number must be positive" except ValueError: return False, f"Invalid cycle number '{cycle_str}'" return True, "Valid tracking title" return ( False, "Title does not match expected format: " "'[AUTO-] (Cycle )' or " "'[AUTO-] Announce: '", ) def validate_automation_tracking_issue( issue_data: dict[str, Any], ) -> list[str]: """Validate a complete automation tracking issue. Args: issue_data: Dictionary containing issue data (title, labels, etc.) Returns: List of validation errors (empty if valid) """ errors: list[str] = [] # Validate title title = issue_data.get("title", "") is_valid, message = validate_tracking_title(title) if not is_valid: errors.append(f"Title format error: {message}") # Validate labels labels = issue_data.get("labels", []) label_names = [ label.get("name", "") if isinstance(label, dict) else str(label) for label in labels ] required_labels = [ "Automation Tracking", "Type/Automation", "State/In Progress", "Priority/Medium", ] for required_label in required_labels: if required_label not in label_names: errors.append(f"Missing required '{required_label}' label") # Validate body content (basic checks) body = issue_data.get("body", "") if not body.strip(): errors.append("Issue body cannot be empty") # Check for common required elements in body if "**Automated by CleverAgents Bot**" not in body: errors.append("Missing automation signature in body") return errors def get_tracking_issues_from_repo( owner: str, repo: str, ) -> list[dict[str, Any]]: """Fetch automation tracking issues from repository. Note: This is a stub — in real usage, integrate with the Forgejo API. Real implementations must paginate results because Forgejo limits responses to 50 items per page by default. """ print(f"Note: Repository validation for {owner}/{repo} requires API integration") print( "This is a demonstration of the validation logic. Real integrations must " "paginate Forgejo API responses (commonly limited to 50 items per page)." ) return [] def _run_validate_all() -> int: """Run comprehensive validation examples and report results.""" print("Running comprehensive validation examples...") # Test cases test_cases = [ # Valid cases ("[AUTO-SESSION] Checkpoint (Cycle 15)", True), ("[AUTO-IMP-POOL] Health Report (Cycle 42)", True), ("[AUTO-WATCHDOG] System Health (Cycle 8)", True), ("[AUTO-GROOMER] Grooming Report (Cycle 23)", True), ("[AUTO-LIAISON] Status Update (Cycle 67)", True), ("[AUTO-DOCS] Documentation Report (Cycle 1)", True), ( "[AUTO-SESSION] Announce: Emergency system restart required", True, ), # Invalid cases ("[AUTO-UNKNOWN] Test (Cycle 1)", False), ("[AUTO-SESSION] Invalid Type (Cycle 1)", False), ("[AUTO-SESSION] Checkpoint (Cycle 0)", False), ("[AUTO-SESSION] Checkpoint", False), ("Regular issue title", False), ("[AUTO-SESSION] Announce:", False), ] print("\nTitle Validation Test Results:") print("-" * 50) all_passed = True for title, expected_valid in test_cases: is_valid, message = validate_tracking_title(title) status = "\u2713" if is_valid == expected_valid else "\u2717" result = "VALID" if is_valid else "INVALID" print(f"{status} {title}") expected_str = "VALID" if expected_valid else "INVALID" print(f" Expected: {expected_str}, Got: {result}") print(f" Message: {message}") print() if is_valid != expected_valid: all_passed = False verdict = "\u2713 ALL PASSED" if all_passed else "\u2717 SOME FAILED" print(f"Overall test result: {verdict}") return 0 if all_passed else 1 def main() -> int: """Entry point for automation tracking validation.""" parser = argparse.ArgumentParser( description="Validate automation tracking issues", ) parser.add_argument( "--title", help="Validate a single title string", ) parser.add_argument( "--repo", help="Validate issues from repository (owner/repo)", ) parser.add_argument( "--validate-all", action="store_true", help="Run comprehensive validation examples", ) args = parser.parse_args() if args.title: is_valid, message = validate_tracking_title(args.title) print(f"Title: {args.title}") valid_str = "\u2713 VALID" if is_valid else "\u2717 INVALID" print(f"Result: {valid_str}") print(f"Message: {message}") return 0 if is_valid else 1 elif args.repo: try: owner, repo_name = args.repo.split("/", 1) get_tracking_issues_from_repo(owner, repo_name) print(f"Repository validation for {args.repo} completed") return 0 except ValueError: print("Error: Repository must be in format 'owner/repo'") return 1 elif args.validate_all: return _run_validate_all() parser.print_help() return 1 if __name__ == "__main__": sys.exit(main())