6559a0e9df
CI / lint (pull_request) Successful in 51s
CI / quality (pull_request) Successful in 49s
CI / typecheck (pull_request) Successful in 58s
CI / security (pull_request) Successful in 53s
CI / build (pull_request) Successful in 24s
CI / push-validation (pull_request) Successful in 20s
CI / helm (pull_request) Successful in 23s
CI / e2e_tests (pull_request) Successful in 4m3s
CI / integration_tests (pull_request) Successful in 8m37s
CI / unit_tests (pull_request) Successful in 11m26s
CI / coverage (pull_request) Successful in 14m48s
CI / docker (pull_request) Successful in 11s
CI / status-check (pull_request) Successful in 1s
CI / lint (push) Successful in 19s
CI / quality (push) Successful in 45s
CI / security (push) Successful in 1m0s
CI / typecheck (push) Successful in 1m29s
CI / build (push) Successful in 39s
CI / helm (push) Successful in 25s
CI / push-validation (push) Successful in 18s
CI / e2e_tests (push) Successful in 4m42s
CI / integration_tests (push) Successful in 7m12s
CI / unit_tests (push) Successful in 8m52s
CI / coverage (push) Successful in 13m35s
CI / docker (push) Successful in 16s
CI / status-check (push) Successful in 1s
- document docs-writer responsibilities and automation tracking requirements\n- enforce automation tracking label validation and clean coverage regression tags\n\nISSUES CLOSED: #7616 # Conflicts: # CHANGELOG.md # docs/development/automation-tracking.md # docs/development/docs-writer.md # mkdocs.yml
259 lines
8.0 KiB
Python
Executable File
259 lines
8.0 KiB
Python
Executable File
#!/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-<PREFIX>] <TYPE> (Cycle <N>)' or "
|
|
"'[AUTO-<PREFIX>] Announce: <message>'",
|
|
)
|
|
|
|
|
|
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())
|