Files
cleveragents-core/scripts/validate_automation_tracking.py
hurui200320 0bd8fbb296 fix(ci): resolve lint errors and remove stale tdd_expected_fail tag (#5264)
## Summary

The `lint` and `integration_tests` CI jobs on `master` are currently failing. This PR fixes both:

1. **Lint (`nox -e lint`)** — 51 ruff violations in `scripts/validate_automation_tracking.py`:
   - Sorted imports per isort convention
   - Replaced deprecated `typing.List`/`Dict`/`Tuple`/`Optional` with builtin equivalents (`list`, `dict`, `tuple`)
   - Removed unused `Optional` import and unused local variable `issues`
   - Fixed trailing whitespace, blank-line whitespace, and 6 lines exceeding 88-char limit
   - Replaced `dict.keys()` with `dict` in membership tests
   - Added return type annotation to `main()`

2. **Integration tests (`nox -e integration_tests`)** — 1 failure in `robot/coverage_threshold.robot`:
   - Removed `tdd_expected_fail`, `tdd_issue`, and `tdd_issue_4305` tags from the `Noxfile Contains Coverage Threshold Constant` test
   - The bug this TDD tag tracked (issue #4305) is now fixed — `COVERAGE_THRESHOLD = 97` exists in `noxfile.py`
   - The TDD expected-fail listener was inverting the passing result to a failure

### Local verification

- `nox -e lint` — All checks passed
- `nox -e integration_tests` — 1962 tests, 1962 passed, 0 failed, 0 skipped

### Files changed

| File | Change |
|------|--------|
| `scripts/validate_automation_tracking.py` | Fixed all 51 lint violations |
| `robot/coverage_threshold.robot` | Removed stale `tdd_expected_fail` tag |

Closes #5266

Reviewed-on: cleveragents/cleveragents-core#5264
Co-authored-by: Rui Hu <rui.hu@cleverthis.com>
Co-committed-by: Rui Hu <rui.hu@cleverthis.com>
2026-04-09 04:41:01 +00:00

244 lines
7.5 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"],
}
# 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
]
if "Automation Tracking" not in label_names:
errors.append("Missing required 'Automation Tracking' 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.
"""
print(f"Note: Repository validation for {owner}/{repo} requires API integration")
print("This is a demonstration of the validation logic.")
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-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
if 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
if args.validate_all:
return _run_validate_all()
parser.print_help()
return 1
if __name__ == "__main__":
sys.exit(main())