97 lines
3.1 KiB
Python
Executable File
97 lines
3.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Normalize gate artifact JSON schema — minimal `agent` field fix.
|
|
|
|
History: F-123..F-135 (13 features x 3 gate files = 39 files) were closed
|
|
with the wrong field name (`reviewer` instead of `agent`). `verify.sh`
|
|
rejects those files because it checks `obj.get('agent') == '<role>'`.
|
|
|
|
This script is idempotent: it adds `agent` from the legacy `reviewer`
|
|
field (when present and matching) without touching any other field.
|
|
Re-running it on an already-normalized file is a no-op. `stage` is not
|
|
required by `verify.sh`, so we leave it alone.
|
|
|
|
Usage:
|
|
python3 scripts/fix_gate_schema.py # all artifacts
|
|
python3 scripts/fix_gate_schema.py F-123 F-124 # specific features
|
|
python3 scripts/fix_gate_schema.py --dry-run # preview only
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
ARTIFACTS_DIR = ROOT / 'work' / 'artifacts'
|
|
|
|
FILE_TO_AGENT = {
|
|
'reviewer.json': 'reviewer',
|
|
'security.json': 'security',
|
|
'qa.json': 'qa',
|
|
}
|
|
|
|
|
|
def normalize(feature_dir: Path, dry_run: bool = False) -> list[str]:
|
|
"""Normalize gate files in `feature_dir`. Returns list of changes made."""
|
|
changes: list[str] = []
|
|
fid = feature_dir.name
|
|
for filename, expected_agent in FILE_TO_AGENT.items():
|
|
path = feature_dir / filename
|
|
if not path.is_file():
|
|
continue
|
|
try:
|
|
data = json.loads(path.read_text(encoding='utf-8'))
|
|
except Exception:
|
|
continue
|
|
|
|
if data.get('agent') == expected_agent:
|
|
continue # already correct, no-op
|
|
|
|
legacy = data.get('reviewer')
|
|
if isinstance(legacy, str) and legacy == expected_agent:
|
|
data['agent'] = legacy
|
|
changes.append(f"{fid}/{filename}: copied agent='{expected_agent}' from legacy 'reviewer' field")
|
|
else:
|
|
data['agent'] = expected_agent
|
|
changes.append(f"{fid}/{filename}: set agent='{expected_agent}'")
|
|
|
|
if not dry_run:
|
|
path.write_text(
|
|
json.dumps(data, indent=2, ensure_ascii=False) + '\n',
|
|
encoding='utf-8',
|
|
)
|
|
return changes
|
|
|
|
|
|
def main() -> int:
|
|
args = [a for a in sys.argv[1:] if not a.startswith('--')]
|
|
dry_run = '--dry-run' in sys.argv
|
|
|
|
if args:
|
|
targets = [ARTIFACTS_DIR / a for a in args]
|
|
for t in targets:
|
|
if not t.is_dir():
|
|
print(f"[WARN] {t} is not a directory, skipping")
|
|
targets = [t for t in targets if t.is_dir()]
|
|
else:
|
|
targets = sorted(p for p in ARTIFACTS_DIR.iterdir() if p.is_dir())
|
|
|
|
total_changes: list[str] = []
|
|
for d in targets:
|
|
total_changes.extend(normalize(d, dry_run=dry_run))
|
|
|
|
if not total_changes:
|
|
print('[OK] All gate files already conform to schema (agent field present)')
|
|
return 0
|
|
|
|
verb = 'Would fix' if dry_run else 'Fixed'
|
|
print(f"{verb} {len(total_changes)} file(s):")
|
|
for c in total_changes:
|
|
print(f" - {c}")
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|