#!/usr/bin/env python3 """Build the bounded fallback/runtime contract for the Alpino operator Space. The Space reads the published alpina.travel KG live and reads editorial Markdown from current GitHub main. This bundle supplies the exact ontology, tool contract, entity/path index and a deterministic fallback graph needed to construct the runtime safely; it is not the primary operator evidence source. """ from __future__ import annotations import hashlib import json import os import shutil import subprocess import sys import tempfile from datetime import datetime, timezone from pathlib import Path from rdflib import Graph SPACE_DIR = Path(__file__).resolve().parent REPO_ROOT = SPACE_DIR.parents[1] SNAPSHOT_DIR = SPACE_DIR / "runtime_snapshot" ADAPTER_ID = "cyberandy/Alpino-e4b-v02" ADAPTER_REVISION = "d5f49f6a85b0c7788f4afbfea4f53d470d2673a3" LIVE_GRAPH_URL = "https://alpina.travel/lungau/data/graph.rdf" def sha256_file(path: Path) -> str: h = hashlib.sha256() with path.open("rb") as handle: for chunk in iter(lambda: handle.read(1024 * 1024), b""): h.update(chunk) return h.hexdigest() def sha256_tree(path: Path) -> tuple[str, int]: h = hashlib.sha256() count = 0 if not path.exists(): return h.hexdigest(), count for file_path in sorted(p for p in path.rglob("*") if p.is_file()): relative = file_path.relative_to(path).as_posix().encode("utf-8") h.update(relative) h.update(b"\0") with file_path.open("rb") as handle: for chunk in iter(lambda: handle.read(1024 * 1024), b""): h.update(chunk) h.update(b"\0") count += 1 return h.hexdigest(), count def git_sha() -> str: override = (os.getenv("ALPINO_SOURCE_COMMIT") or "").strip() if override: return override return subprocess.check_output( ["git", "rev-parse", "HEAD"], cwd=REPO_ROOT, text=True, ).strip() def copy_file(relative: str, destination_root: Path) -> None: source = REPO_ROOT / relative if not source.exists(): raise FileNotFoundError(f"required Space runtime source is missing: {relative}") destination = destination_root / relative destination.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(source, destination) def copy_optional(relative: str, destination_root: Path) -> None: source = REPO_ROOT / relative if not source.exists(): return destination = destination_root / relative destination.parent.mkdir(parents=True, exist_ok=True) if source.is_dir(): shutil.copytree(source, destination, dirs_exist_ok=True) else: shutil.copy2(source, destination) def main() -> None: source_commit = git_sha() with tempfile.TemporaryDirectory(prefix="alpino-space-snapshot-") as temp_dir: temp = Path(temp_dir) staged = temp / "runtime_snapshot" graph_path = staged / "data/lungau/kg-candidate.ttl" records_path = staged / "build/content-records.json" graph_path.parent.mkdir(parents=True, exist_ok=True) records_path.parent.mkdir(parents=True, exist_ok=True) # Materialize a deterministic governed fallback without publication credentials. env = os.environ.copy() env.pop("WORDLIFT_API_KEY", None) command = [ sys.executable, str(REPO_ROOT / "scripts/wordlift/sync_to_wordlift.py"), "--validate-only", "--status", "proposed", "--source-commit", source_commit, "--output", str(graph_path), "--records-output", str(records_path), ] subprocess.run(command, cwd=REPO_ROOT, env=env, check=True) required = [ "tools/aooe_training_runtime.py", "tools/aooe_trace_protocol.py", "tools/compile_webmaster_traces.py", "tools/aooe_content_observer.py", "bcs/webmaster-operations/tool-affordances.json", "data/lungau/entities.yaml", "ontology/core.ttl", "ontology/axioms.ttl", "ontology/shapes.ttl", "scripts/wordlift/sync_to_wordlift.py", ] for relative in required: copy_file(relative, staged) for relative in [ "ontology/imports.ttl", "evidence/reconciliation", "data/lungau/campaigns.yaml", "data/lungau/campaigns.yml", "data/lungau/campaigns.json", "content/campaigns", "content/lungau", ]: copy_optional(relative, staged) graph = Graph().parse(graph_path, format="turtle") contract_path = staged / "bcs/webmaster-operations/tool-affordances.json" entities_path = staged / "data/lungau/entities.yaml" content_path = staged / "content/lungau" content_sha, content_files = sha256_tree(content_path) manifest = { "bundleVersion": "0.5", "runtime": "WebmasterOpenBookRuntime", "modelAdapter": ADAPTER_ID, "modelAdapterRevision": ADAPTER_REVISION, "baseModel": "google/gemma-4-E4B-it", "sourceCommit": source_commit, "generatedAt": datetime.now(timezone.utc).isoformat(), "graphSha256": sha256_file(graph_path), "graphTriples": len(graph), "contractSha256": sha256_file(contract_path), "entitiesSha256": sha256_file(entities_path), "contentSha256": content_sha, "contentFiles": content_files, "operatorEvidence": { "kgReads": "live-published-graph", "liveGraphUrl": LIVE_GRAPH_URL, "contentReads": "github-main-live", "bundleRole": "contract-path-index-and-fallback", }, "mutationBoundary": { "kgMaterialization": "validate-only-fallback", "wordliftCredentialInjected": False, "runtimeActs": "brokered-bounded-draft-pr", "liveKgWrites": False, "mergeAllowed": False, "publishAllowed": False, }, } (staged / "manifest.json").write_text( json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8", ) if SNAPSHOT_DIR.exists(): shutil.rmtree(SNAPSHOT_DIR) shutil.copytree(staged, SNAPSHOT_DIR) print(json.dumps(manifest, indent=2)) print(f"Prepared HF Space runtime contract at {SNAPSHOT_DIR}") if __name__ == "__main__": main()