205 lines
14 KiB
Python
205 lines
14 KiB
Python
"""Package existing lifecycle preflight evidence. Never run its drivers or binaries."""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import re
|
|
import subprocess
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path, PurePosixPath
|
|
|
|
ARCHIVE = Path("harness/experiments/lifecycle-fixture-preflight-20260913")
|
|
SOURCE = Path(".local/nested-lifecycle-native-fixture-20260912")
|
|
GATES = Path(".local/allocation-union-20260912")
|
|
EXPECTED_FIXTURE = "998a2fd3e2474e72fc013f4da422e49bad61e118de62eccfbd71bf06b863ce7f"
|
|
EXPECTED_PROBE = "bf2eb9b908ae36198d15d47ae79d56e4bb181f506598cc4e76a76175041f633e"
|
|
LIMITATIONS = [
|
|
"Evidence packaging only: no compiler, probe, native app, or original driver was executed.",
|
|
"Historical receipts remain verbatim, including preliminary r1 failure and all diagnostics.",
|
|
"No executable, DLL, rlib, target artifact, or toolchain is included; identities remain in original receipts.",
|
|
"Original receipt commands and PowerShell drivers contain original-local paths and layout assumptions; they are provenance, not portable rerun commands.",
|
|
"The original fixture README includes original-local code-path references; its Markdown receipt links resolve within this archive.",
|
|
"Full gate manifests and logs document historical source versions, not current palette/native acceptance.",
|
|
"110 scoped core/config/lock input bytes are identified by manifests; repository source and full build dependencies are not copied here.",
|
|
"No source snapshot was deduplicated or omitted. Small duplicate fixture/probe/driver/receipt files are retained verbatim.",
|
|
]
|
|
README = """# Lifecycle fixture and preflight evidence
|
|
|
|
This archive preserves existing evidence; packaging did not rerun a compiler, probe, application, or driver. It does not establish native rendering, interaction, palette, or release acceptance.
|
|
|
|
Start with the [original fixture README](fixture/README.md), [fixture](fixture/lifecycle-native.arcadia), [identities](fixture/identities.tsv), and [preflight source](fixture/preflight.rs). The original README and every receipt/log/source snapshot are copied byte for byte. Its preparation-stage and later preflight statements retain their original scope.
|
|
|
|
- [Preliminary r1 receipt](fixture/preliminary-admission-42511f0-r1/run.json): launch failed with -1073741515 (0xC0000135), with empty output. This failure is retained.
|
|
- [Preliminary r2 receipt](fixture/preliminary-admission-42511f0-r2/run.json): same identified executable, runtime PATH prefix supplied, exit 0. Self-comparison establishes only the admission checks described in the original README.
|
|
- [Strong preflight build](fixture/strong-preflight-d61-r1/build.json), [run](fixture/strong-preflight-d61-r1/run.json), [assertion output](fixture/strong-preflight-d61-r1/run.stdout.log), and [110 scoped inputs](fixture/strong-preflight-d61-r1/verified-core-config-inputs.json): original successful core preflight evidence, not GUI evidence.
|
|
- [42511f0 input manifest](provenance/full-42511f0.inputs.json), [result](provenance/full-42511f0.result.json), [log](provenance/full-42511f0.log); [d61 input manifest](provenance/full-d61f1f3.inputs.json), [result](provenance/full-d61f1f3.result.json), [log](provenance/full-d61f1f3.log). These are the historical gates referenced by the preflight driver; they do not verify later desktop changes.
|
|
- [Archive manifest](manifest.json) records every included file's original source path, byte count and SHA-256, the excluded executable identity, receipt cross-checks, source identities, limitations, and payload totals. [Packaging/verification helper](archive.py) is the exact helper used to create this archive.
|
|
|
|
Core oracle source: `42511f0f8ae46f29e11ae18c1030face654c2994`. Closed later core-input comparison gate: `d61f1f38779ab70a77efcde523ae31c432aefcfd`. Strong-preflight recorded current source: `aadc6396d39ff336d05381d58d545093d3f37a73`. The fixture README's target renderer was the reviewed f659 union before later corrections. These are different scopes, not interchangeable tested versions.
|
|
|
|
All small duplicates are retained, so relative receipt-directory references still resolve. Original absolute command paths, `.local` code references, toolchain paths, and PowerShell `$PSScriptRoot` layout assumptions are **original-local provenance only**. The copied drivers must not be executed directly from this archive: their relative repository-root assumptions no longer hold. No executable/dependency package is included. Native session work/baseline copies and subsequent palette receipts are outside this archive's scope.
|
|
|
|
From the repository root, use Python 3 with:
|
|
|
|
```text
|
|
python harness/experiments/lifecycle-fixture-preflight-20260913/archive.py verify --root .
|
|
python harness/experiments/lifecycle-fixture-preflight-20260913/archive.py verify --root . --check-sources
|
|
python harness/experiments/lifecycle-fixture-preflight-20260913/archive.py verify-index --root .
|
|
```
|
|
|
|
`verify` is portable and checks exact file inventory, hashes, sizes, and Markdown links. `--check-sources` additionally compares the original local evidence and therefore needs this original checkout's `.local` files. `verify-index` is optional after staging and compares every archive file, including the manifest, to its exact Git index blob; the helper never stages or commits anything. Creation refuses an existing archive directory.
|
|
"""
|
|
|
|
|
|
def identity(data):
|
|
return {"bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()}
|
|
|
|
|
|
def git(root, *args):
|
|
return subprocess.check_output(["git", "-C", str(root), *args])
|
|
|
|
|
|
def read_json(data):
|
|
return json.loads(data.decode("utf-8-sig"))
|
|
|
|
|
|
def safe_path(base, relative):
|
|
rel = PurePosixPath(relative)
|
|
if rel.is_absolute() or ".." in rel.parts or "\\" in relative:
|
|
raise ValueError(f"Unsafe archive path: {relative}")
|
|
target = (base / Path(*rel.parts)).resolve()
|
|
if not target.is_relative_to(base.resolve()):
|
|
raise ValueError(f"Path escapes archive: {relative}")
|
|
return target
|
|
|
|
|
|
def verify(root, check_sources=False, index=False):
|
|
out = root / ARCHIVE
|
|
manifest = read_json((out / "manifest.json").read_bytes())
|
|
expected = {item["path"] for item in manifest["files"]} | {"manifest.json"}
|
|
actual = {p.relative_to(out).as_posix() for p in out.rglob("*") if p.is_file()}
|
|
if actual != expected:
|
|
raise ValueError(f"Inventory mismatch: missing={expected-actual}; extra={actual-expected}")
|
|
total = 0
|
|
for item in manifest["files"]:
|
|
path = safe_path(out, item["path"])
|
|
if path.is_symlink():
|
|
raise ValueError(f"Symlink payload: {path}")
|
|
data = path.read_bytes()
|
|
if identity(data) != {k: item[k] for k in ("bytes", "sha256")}:
|
|
raise ValueError(f"Identity mismatch: {path}")
|
|
total += len(data)
|
|
if check_sources and item["source_kind"] == "original-local":
|
|
if data != safe_path(root, item["source"]).read_bytes():
|
|
raise ValueError(f"Original source changed: {item['source']}")
|
|
if path.suffix.lower() == ".md":
|
|
for link in re.findall(r"\[[^\]]*\]\(([^)]+)\)", data.decode("utf-8-sig")):
|
|
if "://" in link or link.startswith("#"):
|
|
continue
|
|
destination = (path.parent / link.split("#", 1)[0]).resolve()
|
|
if not destination.is_relative_to(out.resolve()) or not destination.is_file():
|
|
raise ValueError(f"Unresolved Markdown link: {item['path']} -> {link}")
|
|
if total != manifest["payload_bytes"] or len(manifest["files"]) != manifest["payload_count"]:
|
|
raise ValueError("Manifest totals mismatch")
|
|
if check_sources:
|
|
for item in manifest["excluded"]:
|
|
if identity(safe_path(root, item["source"]).read_bytes()) != {k: item[k] for k in ("bytes", "sha256")}:
|
|
raise ValueError(f"Excluded original changed: {item['source']}")
|
|
if index:
|
|
for path in sorted(expected):
|
|
repo_path = (ARCHIVE / path).as_posix()
|
|
if git(root, "show", ":" + repo_path) != (out / path).read_bytes():
|
|
raise ValueError(f"Index byte mismatch: {repo_path}")
|
|
return {"verified": True, "payload_count": len(manifest["files"]), "total_files_including_manifest": len(expected), "payload_bytes": total, "check_sources": check_sources, "check_index": index}
|
|
|
|
|
|
def create(root):
|
|
out = root / ARCHIVE
|
|
if out.exists():
|
|
raise FileExistsError(f"Refuse existing archive: {out}")
|
|
sources = sorted((root / SOURCE).rglob("*"))
|
|
payload = {}
|
|
excluded = []
|
|
for path in sources:
|
|
if path.is_symlink():
|
|
raise ValueError(f"Refuse source symlink: {path}")
|
|
if not path.is_file():
|
|
continue
|
|
data = path.read_bytes()
|
|
source = path.relative_to(root).as_posix()
|
|
if path.suffix.lower() == ".exe":
|
|
excluded.append({"source": source, **identity(data), "reason": "Executable excluded; original build/run identity preserved in receipts."})
|
|
else:
|
|
relative = (Path("fixture") / path.relative_to(root / SOURCE)).as_posix()
|
|
payload[relative] = (data, source, "original-local")
|
|
if len(excluded) != 1 or not excluded[0]["source"].endswith("/strong-preflight-d61-r1/lifecycle-preflight.exe"):
|
|
raise ValueError("Unexpected binary inventory")
|
|
for stem in ("full-42511f0", "full-d61f1f3"):
|
|
for suffix in (".inputs.json", ".result.json", ".log"):
|
|
path = root / GATES / (stem + suffix)
|
|
payload["provenance/" + path.name] = (path.read_bytes(), path.relative_to(root).as_posix(), "original-local")
|
|
def data(path):
|
|
return payload["fixture/" + path][0]
|
|
if identity(data("lifecycle-native.arcadia"))["sha256"] != EXPECTED_FIXTURE:
|
|
raise ValueError("Fixture identity differs from reviewed source")
|
|
if identity(data("preflight.rs"))["sha256"] != EXPECTED_PROBE:
|
|
raise ValueError("Probe identity differs from reviewed source")
|
|
r1 = read_json(data("preliminary-admission-42511f0-r1/run.json"))
|
|
r2 = read_json(data("preliminary-admission-42511f0-r2/run.json"))
|
|
strong = read_json(data("strong-preflight-d61-r1/run.json"))
|
|
build = read_json(data("strong-preflight-d61-r1/build.json"))
|
|
if (r1["exit"], r2["exit"], strong["exit"], build["exit"]) != (-1073741515, 0, 0, 0):
|
|
raise ValueError("Unexpected historical receipt outcomes")
|
|
if excluded[0]["sha256"] != strong["binary_sha256"]:
|
|
raise ValueError("Excluded executable identity mismatch")
|
|
checks = []
|
|
for folder, receipt, prefix in (("preliminary-admission-42511f0-r1", r1, ""), ("preliminary-admission-42511f0-r2", r2, ""), ("strong-preflight-d61-r1", strong, "run.")):
|
|
for stream in ("stdout", "stderr"):
|
|
name = folder + "/" + prefix + stream + ".log"
|
|
if identity(data(name))["sha256"] != receipt[stream + "_sha256"]:
|
|
raise ValueError(f"Receipt/log mismatch: {name}")
|
|
checks.append(name)
|
|
for snapshot in ("preliminary-admission-42511f0-r1/fixture-source.arcadia.txt", "strong-preflight-d61-r1/fixture-source.arcadia.txt"):
|
|
if data(snapshot) != data("lifecycle-native.arcadia"):
|
|
raise ValueError(f"Fixture snapshot differs: {snapshot}")
|
|
if data("strong-preflight-d61-r1/preflight-source.rs") != data("preflight.rs"):
|
|
raise ValueError("Probe snapshot mismatch")
|
|
if data("strong-preflight-d61-r1/run-driver.ps1") != data("run-preflight-d61-r1.ps1"):
|
|
raise ValueError("Driver snapshot mismatch")
|
|
scoped = read_json(data("strong-preflight-d61-r1/verified-core-config-inputs.json"))
|
|
if len(scoped) != 110:
|
|
raise ValueError("Scoped input count mismatch")
|
|
for stem in ("full-42511f0", "full-d61f1f3"):
|
|
inputs = read_json(payload["provenance/" + stem + ".inputs.json"][0])["inputs"]
|
|
indexed = {item["path"]: item for item in inputs}
|
|
for item in scoped:
|
|
if any(indexed[item["path"]][key] != item[key] for key in ("bytes", "sha256")):
|
|
raise ValueError(f"Scoped input disagreement: {stem} {item['path']}")
|
|
payload["README.md"] = (README.encode("utf-8"), "generated archive navigation and limitations", "generated")
|
|
payload["archive.py"] = (Path(__file__).read_bytes(), Path(__file__).resolve().relative_to(root).as_posix(), "original-local")
|
|
files = [{"path": path, "source": source, "source_kind": kind, **identity(blob)} for path, (blob, source, kind) in sorted(payload.items())]
|
|
manifest = {"schema": 1, "created_utc": datetime.now(timezone.utc).isoformat(), "scope": "Historical lifecycle fixture/core preflight packaging only", "archive": ARCHIVE.as_posix(), "source_root": SOURCE.as_posix(), "packaging_checkout_head": git(root, "rev-parse", "HEAD").decode().strip(), "historical_sources": {"oracle_core": r1["core_source"], "comparison_gate": build["full_gate_source"], "strong_preflight_recorded_head": build["current_head"]}, "receipt_exits": {"preliminary_r1": r1["exit"], "preliminary_r2": r2["exit"], "strong_build": build["exit"], "strong_run": strong["exit"]}, "cross_checks": {"receipt_log_hashes": checks, "scoped_inputs_matching_both_full_manifests": len(scoped), "fixture_probe_driver_duplicate_snapshots": "equal and preserved"}, "limitations": LIMITATIONS, "excluded": excluded, "payload_count": len(files), "payload_bytes": sum(item["bytes"] for item in files), "files": files}
|
|
out.mkdir(parents=True, exist_ok=False)
|
|
for path, (blob, _, _) in sorted(payload.items()):
|
|
target = safe_path(out, path)
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
with target.open("xb") as stream:
|
|
stream.write(blob)
|
|
with (out / "manifest.json").open("xb") as stream:
|
|
stream.write((json.dumps(manifest, ensure_ascii=False, indent=2) + "\n").encode("utf-8"))
|
|
return verify(root, check_sources=True)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("mode", choices=("create", "verify", "verify-index"))
|
|
parser.add_argument("--root", type=Path, required=True)
|
|
parser.add_argument("--check-sources", action="store_true")
|
|
args = parser.parse_args()
|
|
root = args.root.resolve()
|
|
result = create(root) if args.mode == "create" else verify(root, args.check_sources, args.mode == "verify-index")
|
|
print(json.dumps(result, indent=2))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|