"""Archive f659 source evidence, or verify it; never stage, apply, build, or commit. From repository root: python .local/archive-lifecycle-union-f6595eb.py create python .local/archive-lifecycle-union-f6595eb.py verify # After the integration owner stages the archive: python .local/archive-lifecycle-union-f6595eb.py verify-index The archived tools/ copy supports the same commands from repository root. Creation refuses overwrite. A failed partial archive is retained for diagnosis. """ from pathlib import Path, PurePosixPath from datetime import datetime, timezone import argparse import hashlib import json import subprocess BASE = 'd61f1f38779ab70a77efcde523ae31c432aefcfd' SOURCE = 'f6595ebf66e88ec42c37b5c1a3c014f5e52cb32d' PREFIX = 'harness/experiments/nested-lifecycle-union-20260912' SCRIPT_NAME = 'archive-lifecycle-union-f6595eb.py' PATCH_PATH = 'patches/d61f1f3-to-f6595eb.patch' FILES = { 'crates/oa-desktop/src/behavior.rs': ('0a0c4df479ff1c46f935735e508144ca2ec3052f', 133741), 'crates/oa-desktop/src/behavior/scenario_activation.rs': ('88fa4d6e861fc96f4dc5497088cd9119cf25e151', 17694), 'crates/oa-desktop/src/behavior/scenario_lifecycle.rs': ('6fe044890864ecf3095d0fedf590104f6121abf7', 9348), 'crates/oa-desktop/src/behavior/scenario_lifecycle/render.rs': ('989fb2d5b173fc68e50705f8d406f7786ba9c979', 14820), 'crates/oa-desktop/src/behavior/scenario_lifecycle/render_tests.rs': ('e0d9ee812a49fb1f8b24b0e9e460e08cd42aa34f', 35358), 'crates/oa-desktop/src/behavior/scenario_lifecycle/union_tests.rs': ('86fa0cceff11ff66497c3514e43659a0ca003a52', 18024), } NOTES = { 'source-handoff.md': ('.local/nested-lifecycle-union-handoff-20260912.md', 8668, '2c28f4ffca8e84626cf4bf7580504a008fe971dda7eb0e9b83d94695bcb42410'), 'independent-review.md': ('.local/nested-lifecycle-union-independent-review-20260912.md', 9736, '3b5b116ca7a15f2b5bac313fab9a91966e2fa64f75cc7fc4509332208dd8f29c'), } README = '''# Nested lifecycle / typed activation source union Source: `f6595ebf66e88ec42c37b5c1a3c014f5e52cb32d`. Base: `d61f1f38779ab70a77efcde523ae31c432aefcfd`. - [Source handoff](source-handoff.md) - [Independent source review](independent-review.md) - [Exact committed source snapshot](source/crates/oa-desktop/src/behavior.rs) - [Full-index binary Git patch](patches/d61f1f3-to-f6595eb.patch) - [Payload manifest](manifest.json) - [Archive/verification helper](tools/archive-lifecycle-union-f6595eb.py) The six source files are extracted from committed Git blobs; they are evidence, not an additional production tree. Notes retain their original bytes, including historical local paths and source-scoped statements. The manifest lists portable archive-relative paths plus original source provenance. It does not claim tests, native acceptance, import, or publication succeeded. Subsequent gates remain separate source-identified records owned by the integration owner. From repository root, run the helper with `verify` to compare payload bytes, manifest metadata, source blobs and regenerated patch. After owner-controlled staging, run `verify-index` to additionally compare every payload and manifest with Git index blobs. Neither mode stages or rewrites files. If Git attributes normalize archived bytes, index verification fails; the owner must resolve that explicitly. No patch is applied by this helper. '''.encode('utf-8') def require(condition, message): if not condition: raise RuntimeError(message) def git(root, *args, data=None): return subprocess.run(['git', '--no-pager', *args], cwd=root, input=data, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True).stdout def digest(raw): return {'bytes': len(raw), 'sha256': hashlib.sha256(raw).hexdigest()} def safe_path(base, relative): parts = PurePosixPath(relative) require(not parts.is_absolute() and '..' not in parts.parts and '\\' not in relative, f'Unsafe relative path: {relative}') path = base.joinpath(*parts.parts) require(path.resolve().is_relative_to(base.resolve()), f'Path escapes archive: {relative}') for ancestor in [path, *path.parents]: require(not ancestor.is_symlink(), f'Symlinks are not archive evidence: {ancestor}') if ancestor == base: break return path def committed_payloads(root): require(git(root, 'cat-file', '-t', SOURCE).strip() == b'commit', 'Source is not a commit') require(git(root, 'cat-file', '-t', BASE).strip() == b'commit', 'Base is not a commit') require(git(root, 'rev-parse', SOURCE + '^').decode().strip() == BASE, 'Source parent differs from reviewed base') changed = git(root, 'diff', '--no-ext-diff', '--no-textconv', '--name-only', '-z', BASE, SOURCE, '--').decode().rstrip('\0').split('\0') require(set(changed) == set(FILES) and len(changed) == len(FILES), 'Commit delta differs from the reviewed six files') payloads = {} for path, (oid, size) in FILES.items(): actual = git(root, 'rev-parse', f'{SOURCE}:{path}').decode().strip() require(actual == oid, f'Unexpected committed blob: {path}') require(git(root, 'cat-file', '-t', oid).strip() == b'blob', path) require(int(git(root, 'cat-file', '-s', oid)) == size, path) raw = git(root, 'cat-file', 'blob', oid) require(len(raw) == size, f'Incomplete cat-file data: {path}') require(git(root, 'hash-object', '--stdin', data=raw).decode().strip() == oid, f'Git object identity mismatch: {path}') payloads['source/' + path] = (raw, { 'kind': 'committed-source', 'source_commit': SOURCE, 'source_path': path, 'git_blob': oid, }) # All changed paths are enforced above; full repository diff retains binary, # executable-mode, rename and new-file metadata. No working-tree diff is read. patch = git(root, 'diff', '--binary', '--full-index', '--no-renames', '--diff-algorithm=myers', '--no-indent-heuristic', '--unified=3', '--inter-hunk-context=0', '--no-ext-diff', '--no-textconv', '--no-color', '--src-prefix=a/', '--dst-prefix=b/', BASE, SOURCE, '--') require(patch.startswith(b'diff --git '), 'Missing binary Git patch') payloads[PATCH_PATH] = (patch, {'kind': 'git-binary-patch', 'base_commit': BASE, 'source_commit': SOURCE}) return payloads def expected_paths(): return {'source/' + p for p in FILES} | set(NOTES) | { PATCH_PATH, 'README.md', 'tools/' + SCRIPT_NAME, } def verify(root, destination, index=False): manifest_raw = safe_path(destination, 'manifest.json').read_bytes() manifest = json.loads(manifest_raw) require(manifest['schema'] == 1 and manifest['source_commit'] == SOURCE and manifest['base_commit'] == BASE and manifest['archive_path'] == PREFIX, 'Manifest provenance mismatch') rows = manifest['files'] require({r['path'] for r in rows} == expected_paths() and len(rows) == len(expected_paths()), 'Manifest payload list mismatch') actual = {p.relative_to(destination).as_posix() for p in destination.rglob('*') if p.is_file() or p.is_symlink()} require(actual == expected_paths() | {'manifest.json'}, 'Unexpected/missing archive file') committed = committed_payloads(root) for row in rows: path = safe_path(destination, row['path']) raw = path.read_bytes() require(digest(raw) == {k: row[k] for k in ('bytes', 'sha256')}, f'Payload digest mismatch: {path}') if row['path'] in committed: expected, metadata = committed[row['path']] require(raw == expected, f'Committed bytes/patch mismatch: {path}') require(all(row.get(k) == v for k, v in metadata.items()), f'Provenance mismatch: {path}') elif row['path'] in NOTES: origin, size, sha = NOTES[row['path']] require(digest(raw) == {'bytes': size, 'sha256': sha} and row['source_path'] == origin, f'Approved note changed: {path}') elif row['path'] == 'README.md': require(raw == README, 'Archive navigation changed') if index: indexed = git(root, 'cat-file', 'blob', ':' + PREFIX + '/' + row['path']) require(indexed == raw, f'Git index bytes differ: {path}') if index: require(git(root, 'cat-file', 'blob', ':' + PREFIX + '/manifest.json') == manifest_raw, 'Git index manifest differs') print(f'PASS {len(rows)} payloads: manifest, workspace, committed source and patch' + ('; payloads and manifest also match Git index' if index else '')) def create(root, destination): require(not destination.exists() and not destination.is_symlink(), f'Refusing to overwrite existing archive: {destination}') payloads = committed_payloads(root) # Preflight every approved local input and gather all bytes before creating anything. for target, (origin, size, sha) in NOTES.items(): raw = safe_path(root, origin).read_bytes() require(digest(raw) == {'bytes': size, 'sha256': sha}, f'Approved note changed: {origin}') payloads[target] = (raw, {'kind': 'source-review-record', 'source_path': origin, 'reviewed_source_commit': SOURCE}) payloads['README.md'] = (README, {'kind': 'archive-navigation'}) payloads['tools/' + SCRIPT_NAME] = (Path(__file__).read_bytes(), { 'kind': 'archive-helper', 'source_path': '.local/' + SCRIPT_NAME, }) require(set(payloads) == expected_paths(), 'Internal payload list mismatch') manifest = { 'schema': 1, 'archive_path': PREFIX, 'base_commit': BASE, 'source_commit': SOURCE, 'created_utc': datetime.now(timezone.utc).isoformat(), 'evidence_scope': 'Committed source and source-only review; no combined test/native pass asserted', 'files': [{'path': path, **metadata, **digest(raw)} for path, (raw, metadata) in sorted(payloads.items())], } destination.mkdir(parents=False, exist_ok=False) for relative, (raw, _) in sorted(payloads.items()): target = safe_path(destination, relative) target.parent.mkdir(parents=True, exist_ok=True) with target.open('xb') as stream: stream.write(raw) with (destination / 'manifest.json').open('xb') as stream: stream.write((json.dumps(manifest, indent=2) + '\n').encode('utf-8')) verify(root, destination) print(f'Created {PREFIX}; not staged, applied, tested, integrated or published') def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('mode', choices=('create', 'verify', 'verify-index')) parser.add_argument('--repo', type=Path, default=Path.cwd()) args = parser.parse_args() root = Path(git(args.repo.resolve(), 'rev-parse', '--show-toplevel').decode().strip()).resolve() destination = safe_path(root, PREFIX) require(destination.parent.is_dir(), 'Existing harness/experiments directory required') if args.mode == 'create': create(root, destination) else: require(destination.is_dir(), 'Archive does not exist') verify(root, destination, index=args.mode == 'verify-index') if __name__ == '__main__': main()