156 lines
7.5 KiB
Python
156 lines
7.5 KiB
Python
"""Archive only the closed, identified canvas native trial; never stage/commit."""
|
|
from pathlib import Path
|
|
from datetime import datetime, timezone
|
|
import hashlib
|
|
import json
|
|
|
|
root = Path(__file__).resolve().parent.parent
|
|
trial = root / '.local/native-qa/canvas-paint-d61f1f3-20260912-01'
|
|
stage = root / '.local/native-qa/studio-d61f1f3'
|
|
prefix = 'harness/experiments/canvas-native-d61f1f3-20260912'
|
|
destination = root / prefix
|
|
source = 'd61f1f38779ab70a77efcde523ae31c432aefcfd'
|
|
result_path = root / '.local/canvas-native-d61f1f3-archive-result.json'
|
|
assert not destination.exists() and not result_path.exists(), 'Never overwrite an archive/run'
|
|
assert destination.resolve().is_relative_to(root / 'harness/experiments')
|
|
|
|
|
|
def digest(path):
|
|
raw = path.read_bytes()
|
|
return {'bytes': len(raw), 'sha256': hashlib.sha256(raw).hexdigest()}
|
|
|
|
|
|
def read(path):
|
|
return json.loads(path.read_bytes())
|
|
|
|
|
|
def verify(path, record):
|
|
actual = digest(path)
|
|
assert actual['sha256'] == record['sha256'], str(path)
|
|
if 'bytes' in record:
|
|
assert actual['bytes'] == record['bytes'], str(path)
|
|
|
|
|
|
def write_new(path, record):
|
|
with path.open('x', encoding='utf-8', newline='\n') as stream:
|
|
stream.write(json.dumps(record, indent=2) + '\n')
|
|
|
|
|
|
launch = read(trial / 'launch.json')
|
|
closure = read(trial / 'closure.json')
|
|
prepared = read(trial / 'preparation.json')
|
|
staged = read(stage / 'manifest.json')
|
|
assert launch['source'] == staged['source'] == staged['full_test_source'] == source
|
|
assert closure['pid'] == launch['pid'] and closure['exit'] == 0
|
|
assert prepared['utc'] < launch['utc'] < closure['utc']
|
|
assert Path(launch['binary']).resolve() == (stage / 'arcadia-studio.exe').resolve()
|
|
assert launch['sha256'] == staged['artifacts']['arcadia-studio.exe']['sha256']
|
|
for name in ['arcadia-studio.exe', 'libunwind.dll']:
|
|
verify(stage / name, staged['artifacts'][name])
|
|
for name in ['full', 'clippy', 'gui']:
|
|
gate = staged['results'][name]
|
|
assert gate['source'] == source and gate['exit'] == 0 and not gate['changed_inputs']
|
|
|
|
baseline_names = ['bound-baseline']
|
|
baseline_records = [row for row in prepared['files'] if Path(row['path']).parts[0] in baseline_names]
|
|
for row in baseline_records:
|
|
verify(trial / row['path'], row)
|
|
# Original baseline sources are independently retained elsewhere too.
|
|
verify(root / row['source'], row)
|
|
assert {row['path'] for row in baseline_records} == {
|
|
path.relative_to(trial).as_posix() for name in baseline_names
|
|
for path in (trial / name).rglob('*') if path.is_file()}
|
|
|
|
checkpoint_records = []
|
|
checkpoint_files = set()
|
|
oracle_identities = {}
|
|
for path in sorted((trial / 'evidence').glob('*.json')):
|
|
record = read(path)
|
|
assert set(['snapshot', 'command', 'checker_sha256', 'exit', 'files']) <= record.keys(), str(path)
|
|
assert record['exit'] == 0
|
|
snapshot = Path(record['snapshot']).resolve()
|
|
assert snapshot == (trial / 'checkpoints' / path.stem).resolve()
|
|
assert record['utc'] <= closure['utc']
|
|
actual = {p.resolve() for p in snapshot.rglob('*') if p.is_file()}
|
|
declared = {(snapshot / row['path']).resolve() for row in record['files']}
|
|
assert actual == declared
|
|
for row in record['files']:
|
|
item = (snapshot / row['path']).resolve()
|
|
assert item.is_relative_to(snapshot)
|
|
verify(item, row)
|
|
checker = Path(record['command'][0])
|
|
verify(checker, {'sha256': record['checker_sha256']})
|
|
oracle_identities[str(checker.relative_to(root))] = digest(checker)
|
|
assert (trial / 'evidence' / (path.stem + '.log')).is_file()
|
|
checkpoint_files.update(actual)
|
|
checkpoint_records.append(path.relative_to(trial).as_posix())
|
|
assert checkpoint_files == {p.resolve() for p in (trial / 'checkpoints').rglob('*') if p.is_file()}
|
|
|
|
payloads = []
|
|
for name in ['preparation.json', 'launch.json', 'closure.json', 'stdout.log', 'stderr.log']:
|
|
payloads.append((trial / name, Path(name)))
|
|
for name in ['evidence', 'checkpoints', *baseline_names]:
|
|
for path in sorted((trial / name).rglob('*')):
|
|
if path.is_file():
|
|
assert path.resolve().is_relative_to(trial) and not path.is_symlink()
|
|
if name == 'evidence':
|
|
assert path.suffix.lower() in ['.png', '.json', '.log'], path
|
|
payloads.append((path, path.relative_to(trial)))
|
|
for name in ['canvas-native-paint-session.py', 'canvas-paint-checkpoint.py', 'canvas-native-checkpoint.py', Path(__file__).name]:
|
|
payloads.append((root / '.local' / name, Path('tools') / name))
|
|
payloads.append((stage / 'manifest.json', Path('stage/manifest.json')))
|
|
assert len({relative.as_posix() for _, relative in payloads}) == len(payloads)
|
|
assert not any(relative.parts[0] in ['profile', 'temp', 'compact-work', 'bound-work',
|
|
'draft-work', 'process-work'] for _, relative in payloads)
|
|
before = {str(path): digest(path) for path, _ in payloads}
|
|
|
|
destination.mkdir()
|
|
rows = []
|
|
for path, relative in payloads:
|
|
target = destination / relative
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
raw = path.read_bytes()
|
|
with target.open('xb') as stream:
|
|
stream.write(raw)
|
|
verify(target, before[str(path)])
|
|
rows.append({'path': relative.as_posix(), 'source_path': path.relative_to(root).as_posix(),
|
|
**before[str(path)]})
|
|
for path, _ in payloads:
|
|
verify(path, before[str(path)])
|
|
|
|
# Only append a scoped rule; never rewrite or normalize existing attributes.
|
|
attributes = root / '.gitattributes'
|
|
attribute_before = digest(attributes)
|
|
rule = prefix + '/** -text whitespace=-blank-at-eol,-blank-at-eof,cr-at-eol'
|
|
current = attributes.read_bytes()
|
|
added = rule.encode() not in current.splitlines()
|
|
if added:
|
|
assert (prefix + '/').encode() not in current, 'Review an existing prefix rule instead of replacing it'
|
|
with attributes.open('ab') as stream:
|
|
if current and not current.endswith(b'\n'):
|
|
stream.write(b'\n')
|
|
stream.write(rule.encode() + b'\n')
|
|
|
|
manifest = {'archived_utc': datetime.now(timezone.utc).isoformat(), 'source': source,
|
|
'native_trial': trial.relative_to(root).as_posix(), 'native_pid': launch['pid'],
|
|
'native_exit': closure['exit'], 'payload_count': len(rows),
|
|
'payload_bytes': sum(row['bytes'] for row in rows),
|
|
'verified_checkpoint_records': checkpoint_records, 'verified_baseline_files': len(baseline_records),
|
|
'oracle_binary_identities_not_native_app_source': oracle_identities,
|
|
'exclusions': ['profile', 'temp', 'compact-work', 'bound-work', 'draft-work', 'process-work'],
|
|
'byte_policy': 'Copied source bytes exactly, including CRLF; originals unmodified. Source paths are provenance, not portable execution paths.',
|
|
'attributes': {'rule': rule, 'appended': added, 'before': attribute_before, 'after': digest(attributes)},
|
|
'files': rows}
|
|
write_new(destination / 'manifest.json', manifest)
|
|
assert {p.relative_to(destination).as_posix() for p in destination.rglob('*') if p.is_file()} == {
|
|
row['path'] for row in rows} | {'manifest.json'}
|
|
result = {'archive': prefix, 'source': source, 'native_exit': closure['exit'],
|
|
'payload_count': len(rows), 'archive_file_count_including_manifest': len(rows) + 1,
|
|
'screenshots': sum(row['path'].endswith('.png') for row in rows),
|
|
'checkpoint_count': len(checkpoint_records), 'payload_bytes': manifest['payload_bytes'],
|
|
'all_payload_source_and_destination_hashes_equal': True,
|
|
'manifest': digest(destination / 'manifest.json'), 'helper': digest(Path(__file__)),
|
|
'attributes_rule_appended': added, 'git_staging_or_commit_performed': False}
|
|
write_new(result_path, result)
|
|
print(json.dumps(result, indent=2))
|