Files
ARCADIA-Studio/harness/experiments/contextual-prerelease-20260911/producer/readback.py
T

65 lines
3.4 KiB
Python

"""Producer non-execution readback; independent review remains separate."""
import hashlib
import json
from pathlib import Path
import subprocess
import zipfile
from datetime import datetime, timezone
BASE = Path(__file__).resolve().parent
NAME = 'ArcadiaStudio-0.2.0-preview.contextual.20260911-x86_64-pc-windows-gnullvm'
ZIP = BASE / 'packages' / (NAME + '.zip')
STAGE = BASE / 'packages' / NAME
EXPECTED = '9186d4fc9a7b52fb195c183c4847c8e3419ea6da8b3f907e0c06f9ceec58dee0'
def sha(path):
with path.open('rb') as stream:
return hashlib.file_digest(stream, 'sha256').hexdigest()
assert sha(ZIP) == EXPECTED
result = {'read_at': datetime.now(timezone.utc).isoformat(),
'archive': str(ZIP), 'archive_sha256': EXPECTED, 'archive_bytes': ZIP.stat().st_size}
actual = {}
with zipfile.ZipFile(ZIP) as archive:
names = archive.namelist()
assert len(names) == len(set(names)) == len({n.casefold() for n in names}) == 559
for entry in archive.infolist():
assert entry.filename.startswith(NAME + '/') and not entry.is_dir()
relative = entry.filename[len(NAME) + 1:]
assert '..' not in Path(relative).parts and not Path(relative).is_absolute()
assert entry.date_time == (2000, 1, 1, 0, 0, 0)
with archive.open(entry) as stream:
checksum = hashlib.file_digest(stream, 'sha256').hexdigest()
assert checksum == sha(STAGE / relative), relative
actual[relative] = {'bytes': entry.file_size, 'sha256': checksum}
assert set(actual) == {p.relative_to(STAGE).as_posix() for p in STAGE.rglob('*') if p.is_file()}
expected_manifest = {}
for line in (STAGE / 'SHA256SUMS.txt').read_text(encoding='utf-8').splitlines():
checksum, path = line.split(' ', 1)
assert path not in expected_manifest
expected_manifest[path] = checksum
assert set(expected_manifest) == set(actual) - {'SHA256SUMS.txt'}
for path, checksum in expected_manifest.items():
assert actual[path]['sha256'] == checksum
package = json.loads((STAGE / 'package.json').read_text(encoding='utf-8-sig'))
notices = json.loads((STAGE / 'dependency-notices.json').read_text(encoding='utf-8-sig'))
docs = json.loads((STAGE / 'documentation-check.json').read_text(encoding='utf-8-sig'))
prepared = json.loads((BASE / 'preparation.json').read_text(encoding='utf-8'))
for path, checksum in prepared['gui_inputs'].items():
assert sha(BASE / 'source' / path) == checksum
for path, checksum in prepared['server_inputs'].items():
assert sha(BASE / 'source' / path) == checksum
for path, checksum in prepared['shared_non_cargo_inputs'].items():
assert sha(BASE / 'source' / path) == checksum
assert not subprocess.check_output(['git', 'status', '--porcelain'], cwd=BASE / 'source')
result.update({'files': actual, 'file_count': len(actual), 'manifest_entries': len(expected_manifest),
'dependency_packages': len(notices['packages']), 'notice_roots': notices['root_packages'],
'documentation_check': docs, 'binary_imports': package['imports'],
'source_commit': prepared['reconstruction_commit'], 'gui_inputs_unchanged': 238,
'server_inputs_unchanged': 120, 'shared_inputs_unchanged': 5,
'source_worktree_clean': True, 'scope': 'Producer readback only; no extraction or executable/runtime launch.'})
with (BASE / 'package-readback.json').open('x', encoding='utf-8', newline='\n') as output:
json.dump(result, output, indent=2)
output.write('\n')
print(json.dumps({k: v for k, v in result.items() if k != 'files'}, indent=2))