import hashlib import json from pathlib import Path from datetime import datetime, timezone root = Path(__file__).resolve().parents[1] review = root / 'artifacts/verification/VisualUpgrade/ModelReview' packet_path = review / 'review-packet.json' packet = json.loads(packet_path.read_text(encoding='utf-8')) approved = [r for r in packet['files'] if r['relativePath'].lower().endswith(('.blend', '.fbx'))] assert len(approved) == 25, len(approved) for record in approved: path = (review / record['relativePath']).resolve() assert path.stat().st_size == record['bytes'] assert hashlib.sha256(path.read_bytes()).hexdigest() == record['sha256'], str(path) now = datetime.now(timezone.utc).isoformat() approval = {'status': 'APPROVED', 'userResponse': '\uc2b9\uc778\ud569\ub2c8\ub2e4', 'confirmedUtc': now, 'scope': 'All five model groups presented in the immediately preceding review', 'groups': [g['id'] for g in packet['reviewGroups']], 'reviewPacketSha256': hashlib.sha256(packet_path.read_bytes()).hexdigest(), 'reviewPacket': 'review-packet.json', 'approvedFiles': approved} (review / 'user-approval.json').write_text(json.dumps(approval, ensure_ascii=False, indent=2), encoding='utf-8') state_path = root / '.dryforge/execution.json' state = json.loads(state_path.read_text(encoding='utf-8')) state['tasks']['T3'] = 'completed_explicit_user_approval' state['model_approval'] = approval state_path.write_text(json.dumps(state, ensure_ascii=False, indent=2), encoding='utf-8') print('EXPLICIT_USER_APPROVAL_RECORDED; 25 original/model hashes matched; 5 groups approved')