feat(F-137): completed feature
This commit is contained in:
96
scripts/fix_gate_schema.py
Executable file
96
scripts/fix_gate_schema.py
Executable file
@@ -0,0 +1,96 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user