← Files Career CentreARCHIVED FILE
scripts/record_visual_qa.py
3.98 KB · Sep 30, 2026 · 23:13 UTC
#!/usr/bin/env python3
"""Record a human/agent visual review and reconcile an application-pack manifest."""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
def _sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--report", required=True)
parser.add_argument("--status", choices=["passed", "failed"], required=True)
parser.add_argument("--reviewer", required=True)
parser.add_argument("--note", required=True)
parser.add_argument("--manifest")
args = parser.parse_args()
report_path = Path(args.report).resolve()
report = json.loads(report_path.read_text(encoding="utf-8"))
if args.status == "passed" and not report.get("layout_pass"):
raise SystemExit("Cannot record visual pass when automated layout checks failed.")
images = [
(Path(str(page.get("image"))) if Path(str(page.get("image"))).is_absolute() else report_path.parent / str(page.get("image")))
for page in report.get("pages", [])
if page.get("image")
]
missing_images = [str(path) for path in images if not path.exists()]
if args.status == "passed" and (not images or missing_images):
raise SystemExit("Cannot record visual pass without all rendered page images.")
report["visual_inspection"] = args.status
report["visual_reviewer"] = args.reviewer
report["visual_review_note"] = args.note
report_path.write_text(json.dumps(report, indent=2), encoding="utf-8")
manifest_path = Path(args.manifest).resolve() if args.manifest else report_path.parent.parent / "application_pack_manifest.json"
if manifest_path.exists():
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
render_reports = set(manifest.get("render_reports", []))
relative_report = str(report_path.relative_to(manifest_path.parent))
render_reports.add(relative_report)
manifest["render_reports"] = sorted(render_reports)
def resolved_file(item: dict[str, str]) -> Path:
raw = Path(str(item.get("path", "")))
return raw if raw.is_absolute() else manifest_path.parent / raw
files = [item for item in manifest.get("files", []) if resolved_file(item).resolve() != report_path]
files.append({"name": report_path.name, "path": relative_report, "sha256": _sha256(report_path)})
manifest["files"] = sorted(files, key=lambda item: item["path"])
required_docx = [manifest.get("cv")]
if not manifest.get("cv_only"):
required_docx.append(manifest.get("cover_letter"))
required_docx = [name for name in required_docx if name]
passed_sources: set[str] = set()
all_reports_pass = True
for relative in manifest["render_reports"]:
candidate_path = manifest_path.parent / relative
if not candidate_path.exists():
all_reports_pass = False
continue
candidate = json.loads(candidate_path.read_text(encoding="utf-8"))
source_name = Path(str(candidate.get("source_docx", ""))).name
if candidate.get("layout_pass") and candidate.get("visual_inspection") == "passed":
passed_sources.add(source_name)
else:
all_reports_pass = False
if all_reports_pass and set(required_docx).issubset(passed_sources):
manifest["visual_qa"] = "passed"
if manifest.get("status") == "BUILT_STRUCTURAL_PASS":
manifest["status"] = "READY"
elif args.status == "failed":
manifest["visual_qa"] = "failed"
manifest["status"] = "FAILED_VISUAL_QA"
else:
manifest["visual_qa"] = "pending"
manifest_path.write_text(json.dumps(manifest, indent=2), encoding="utf-8")
print(json.dumps(report, indent=2))
return 0 if args.status == "passed" else 1
if __name__ == "__main__":
raise SystemExit(main())
SHA-256: 30f398fcd060f0668399e97febfba47821c5e5403fec4275d2bfea9ca65f7bc5