LightNest / experiments /dependency_package.py
LIghtJUNction's picture
Integrate latest architecture breakthrough, harder tasks, and evaluation suite
e64c5d2 verified
Raw History Blame Contribute Delete
10.1 kB
"""Package and verify the completed dependency experiment without prior archives.
The archive includes the current source tree, this entire experiment, the two
frozen starting predictors, and the prior public data/second VM needed here.
It reproduces this round from the included parents, not every older experiment.
No package operation is allowed before the independent final audit succeeds.
"""
from __future__ import annotations
import argparse
from datetime import datetime, timezone
import hashlib
import io
import json
import lzma
import os
from pathlib import Path
import subprocess
import tarfile
ROOT = Path(__file__).resolve().parents[1]
WORK = ROOT / 'work/morphology-20261017'
def sha(path):
result = hashlib.sha256()
with Path(path).open('rb') as source:
for block in iter(lambda: source.read(1024 * 1024), b''):
result.update(block)
return result.hexdigest()
def write_json(path, value):
with Path(path).open('x') as output:
json.dump(value, output, ensure_ascii=False, sort_keys=True, indent=2)
output.write('\n')
def collect(bundle):
git_files = subprocess.check_output(
['git', 'ls-files', '-z', '--cached', '--others', '--exclude-standard'], cwd=ROOT)
paths = {ROOT / item.decode() for item in git_files.split(b'\0') if item}
paths.update(path for path in WORK.rglob('*') if path.is_file()
and '__pycache__' not in path.parts and not path.name.endswith('.pyc'))
dependencies = [
'work/architecture-20261017/audit_evaluation.py',
'work/architecture-20261017/data/train.jsonl',
'work/architecture-20261017/data/development.jsonl',
'work/architecture-20261017/data/development-episodes.json',
'work/architecture-20261017/data-protocol.json',
'work/architecture-20261017/relation/formal-protocol.json',
]
for seed in (104729, 130363):
for name in ('final.pt', 'protocol.json', 'report.json', 'training-order.json'):
dependencies.append(f'work/architecture-20261017/relation/formal-dataflow-seed{seed}/{name}')
paths.update(ROOT / relative for relative in dependencies)
result = []
for path in sorted(paths):
if path.is_symlink() or not path.is_file() or not path.resolve().is_relative_to(ROOT):
raise ValueError('unexpected missing or linked input: ' + str(path))
result.append(dict(path=path, name='lightnest/' + str(path.relative_to(ROOT)),
bytes=path.stat().st_size, sha256=sha(path), mode=path.stat().st_mode & 0o777))
if not bundle.is_file():
raise ValueError('missing source-history bundle')
result.append(dict(path=bundle, name='lightnest/source-history.bundle',
bytes=bundle.stat().st_size, sha256=sha(bundle), mode=0o644))
if len({entry['name'] for entry in result}) != len(result):
raise ValueError('duplicate archive names')
return result
def verify(archive):
seen = {}
manifest = None
with lzma.open(archive, 'rb') as compressed:
with tarfile.open(fileobj=compressed, mode='r|') as tar:
for member in tar:
if not member.isfile() or member.name in seen or member.name.startswith('/') or '..' in Path(member.name).parts:
raise ValueError('unexpected or duplicate archive member')
stream = tar.extractfile(member)
digest, size = hashlib.sha256(), 0
capture = bytearray() if member.name == 'MANIFEST.json' else None
for block in iter(lambda: stream.read(1024 * 1024), b''):
digest.update(block)
size += len(block)
if capture is not None:
capture.extend(block)
if size != member.size:
raise ValueError('truncated archive member')
seen[member.name] = dict(bytes=size, sha256=digest.hexdigest(), mode=member.mode)
if capture is not None:
manifest = json.loads(capture)
if manifest is None:
raise ValueError('archive manifest missing')
expected = {entry['name']: {key: entry[key] for key in ('bytes', 'sha256', 'mode')}
for entry in manifest['files']}
if set(seen) != set(expected) | {'MANIFEST.json'}:
raise ValueError('archive manifest membership differs')
for name, entry in expected.items():
if seen[name] != entry:
raise ValueError('archive member differs from committed input: ' + name)
return dict(all_checks_passed=True, archive=str(archive), archive_bytes=archive.stat().st_size,
archive_sha256=sha(archive), file_count=len(expected),
payload_bytes=sum(entry['bytes'] for entry in expected.values()),
complete_member_hash_size_and_mode_check=True,
source_commit=manifest['source_commit'],
scope=manifest['scope'])
def package(output, bundle, training_audit):
if output.exists() or output.with_suffix('.verification.json').exists():
raise ValueError('refuse to replace a delivered package')
final_audit = json.loads((WORK / 'independent-audit.json').read_text())
train_audit = json.loads(training_audit.read_text())
if (final_audit.get('all_checks_passed') is not True or
train_audit.get('schema') != 'lightnest-independent-dependency-training-audit-v1' or
train_audit.get('passed') is not True or
train_audit.get('complete_run_count') != 12):
raise ValueError('completed independent training and final audits are required')
inventory_path = WORK / 'models-frozen.json'
inventory = json.loads(inventory_path.read_text())
if (final_audit['inventory_sha256'] != sha(inventory_path) or
train_audit['protocol'] != inventory['training_protocol']):
raise ValueError('audit commitments do not match the frozen experiment')
committed = [inventory['training_protocol'], inventory['data_protocol'],
inventory['specification'], *inventory['role_reports'],
*inventory['sources'], *inventory['models'],
*final_audit['artifacts'], *train_audit['verified_inputs'],
*inventory['suites'].values()]
for record in committed:
path = (ROOT / record['path']).resolve()
if (not path.is_relative_to(ROOT) or not path.is_file() or
sha(path) != record['sha256'] or
('bytes' in record and path.stat().st_size != record['bytes'])):
raise ValueError('audited evidence changed before packaging: ' + record['path'])
subprocess.run(['git', 'bundle', 'verify', str(bundle)], cwd=ROOT, check=True,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
entries = collect(bundle)
manifest = dict(schema='lightnest-dependency-delivery-v1',
created_utc=datetime.now(timezone.utc).isoformat(),
source_commit=subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=ROOT, text=True).strip(),
scope='Current source and complete dependency-round evidence, including starting parent '
'predictors, required prior public data, and the second VM. Earlier full experimental '
'histories remain in their separate preserved archives.',
model_inventory_sha256=sha(WORK / 'models-frozen.json'),
final_audit_sha256=sha(WORK / 'independent-audit.json'),
training_audit_sha256=sha(training_audit),
files=[{key: value for key, value in entry.items() if key != 'path'} for entry in entries])
payload = (json.dumps(manifest, ensure_ascii=False, sort_keys=True, separators=(',', ':')) + '\n').encode()
output.parent.mkdir(parents=True, exist_ok=True)
# Keep the bounded (~120 MB input) delivery in memory until its exact
# compressed size is known. Avoid partially filling the nearly full scratch
# disk. This does not change any experiment member or its committed bytes.
with io.BytesIO() as packed:
with lzma.LZMAFile(packed, 'wb', preset=9) as compressed:
with tarfile.open(fileobj=compressed, mode='w|') as tar:
item = tarfile.TarInfo('MANIFEST.json')
item.size, item.mode, item.mtime = len(payload), 0o644, 0
tar.addfile(item, io.BytesIO(payload))
for entry in entries:
info = tar.gettarinfo(str(entry['path']), arcname=entry['name'])
info.uid = info.gid = 0
info.uname = info.gname = ''
info.mode = entry['mode']
with entry['path'].open('rb') as source:
tar.addfile(info, source)
required = packed.tell()
filesystem = os.statvfs(output.parent)
available = filesystem.f_bavail * filesystem.f_frsize
if required + 1024 * 1024 > available:
raise ValueError(f'archive needs {required} bytes plus 1 MiB reserve; available {available}')
with output.open('xb') as raw:
with packed.getbuffer() as payload_view:
raw.write(payload_view)
raw.flush()
os.fsync(raw.fileno())
checked = verify(output)
write_json(output.with_suffix('.verification.json'), checked)
return checked
def main():
parser = argparse.ArgumentParser()
parser.add_argument('action', choices=('package', 'verify'))
parser.add_argument('--archive', type=Path, required=True)
parser.add_argument('--bundle', type=Path)
parser.add_argument('--training-audit', type=Path)
args = parser.parse_args()
if args.action == 'package':
if args.bundle is None or args.training_audit is None:
parser.error('package requires --bundle and --training-audit')
result = package(args.archive.resolve(), args.bundle.resolve(), args.training_audit.resolve())
else:
result = verify(args.archive.resolve())
print(json.dumps(result, sort_keys=True))
if __name__ == '__main__':
main()