feat(lab): publish M4.7 Worker graph evidence
This commit is contained in:
@@ -153,6 +153,103 @@ def seal_reference_graph_result(
|
||||
)
|
||||
|
||||
|
||||
def read_reference_graph_result(result_root: Path) -> SealedReferenceGraphResult:
|
||||
candidate = result_root.expanduser().absolute()
|
||||
if candidate.is_symlink():
|
||||
raise ReferenceGraphResultError("reference graph result root is invalid")
|
||||
resolved = candidate.resolve(strict=True)
|
||||
if (
|
||||
resolved.is_symlink()
|
||||
or not resolved.is_dir()
|
||||
or not resolved.name.startswith(REFERENCE_GRAPH_RESULT_PREFIX)
|
||||
):
|
||||
raise ReferenceGraphResultError("reference graph result root is invalid")
|
||||
manifest = _read_json(resolved / "manifest.json")
|
||||
expected_manifest_keys = {
|
||||
"schema_version",
|
||||
"result_id",
|
||||
"identity_sha256",
|
||||
"graph_id",
|
||||
"source_profile_id",
|
||||
"run_mode",
|
||||
"canonical_payload_sha256",
|
||||
"runtime",
|
||||
"files",
|
||||
"accepted",
|
||||
}
|
||||
if set(manifest) != expected_manifest_keys:
|
||||
raise ReferenceGraphResultError("reference graph manifest fields changed")
|
||||
if manifest.get("schema_version") != REFERENCE_GRAPH_MANIFEST_SCHEMA:
|
||||
raise ReferenceGraphResultError("reference graph manifest schema changed")
|
||||
if not isinstance(manifest.get("accepted"), bool):
|
||||
raise ReferenceGraphResultError("reference graph acceptance type changed")
|
||||
identity = {
|
||||
key: manifest[key]
|
||||
for key in (
|
||||
"graph_id",
|
||||
"source_profile_id",
|
||||
"run_mode",
|
||||
"canonical_payload_sha256",
|
||||
"runtime",
|
||||
"files",
|
||||
)
|
||||
}
|
||||
identity_sha256 = hashlib.sha256(_canonical_json(identity)).hexdigest()
|
||||
if (
|
||||
manifest.get("result_id") != resolved.name
|
||||
or manifest.get("identity_sha256") != identity_sha256
|
||||
or resolved.name != f"{REFERENCE_GRAPH_RESULT_PREFIX}{identity_sha256}"
|
||||
):
|
||||
raise ReferenceGraphResultError("reference graph identity changed")
|
||||
runtime = ReferenceGraphRuntimeIdentity.from_dict(manifest.get("runtime"))
|
||||
files = manifest.get("files")
|
||||
if not isinstance(files, dict) or set(files) != {
|
||||
"frames.jsonl",
|
||||
"outcomes.jsonl",
|
||||
"report.json",
|
||||
"runtime.json",
|
||||
}:
|
||||
raise ReferenceGraphResultError("reference graph artifact inventory changed")
|
||||
for name, descriptor_value in files.items():
|
||||
if not isinstance(descriptor_value, dict) or set(descriptor_value) != {
|
||||
"sha256",
|
||||
"bytes",
|
||||
}:
|
||||
raise ReferenceGraphResultError("reference graph artifact proof changed")
|
||||
path = _safe_result_file(resolved, name)
|
||||
if descriptor_value.get("bytes") != path.stat().st_size or descriptor_value.get(
|
||||
"sha256"
|
||||
) != _sha256_file(path):
|
||||
raise ReferenceGraphResultError("reference graph artifact proof does not match")
|
||||
runtime_document = _read_json(resolved / "runtime.json")
|
||||
if runtime_document != runtime.to_dict():
|
||||
raise ReferenceGraphResultError("reference graph runtime artifact changed")
|
||||
report = _read_json(resolved / "report.json")
|
||||
if (
|
||||
report.get("schema_version") != REFERENCE_GRAPH_REPORT_SCHEMA
|
||||
or report.get("graph_id") != manifest.get("graph_id")
|
||||
or report.get("canonical_payload_sha256") != manifest.get("canonical_payload_sha256")
|
||||
or not isinstance(report.get("execution"), dict)
|
||||
or report["execution"].get("runtime_identity") != runtime_document
|
||||
or report.get("accepted") is not manifest.get("accepted")
|
||||
):
|
||||
raise ReferenceGraphResultError("reference graph report changed")
|
||||
gates = report.get("gates")
|
||||
if (
|
||||
not isinstance(gates, dict)
|
||||
or not gates
|
||||
or manifest.get("accepted") is not all(value is True for value in gates.values())
|
||||
):
|
||||
raise ReferenceGraphResultError("reference graph acceptance proof changed")
|
||||
return SealedReferenceGraphResult(
|
||||
result_id=resolved.name,
|
||||
result_root=resolved,
|
||||
accepted=bool(manifest["accepted"]),
|
||||
report=report,
|
||||
manifest=manifest,
|
||||
)
|
||||
|
||||
|
||||
def _write_json_lines(path: Path, rows: tuple[dict[str, object], ...]) -> None:
|
||||
with path.open("wb") as handle:
|
||||
for row in rows:
|
||||
@@ -168,6 +265,28 @@ def _write_json(path: Path, document: dict[str, object]) -> None:
|
||||
os.fsync(handle.fileno())
|
||||
|
||||
|
||||
def _read_json(path: Path) -> dict[str, object]:
|
||||
if path.is_symlink() or not path.is_file() or path.stat().st_size > 1024 * 1024:
|
||||
raise ReferenceGraphResultError("reference graph JSON artifact is invalid")
|
||||
try:
|
||||
document = json.loads(path.read_text("utf-8"))
|
||||
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as error:
|
||||
raise ReferenceGraphResultError("reference graph JSON artifact is unreadable") from error
|
||||
if not isinstance(document, dict) or any(not isinstance(key, str) for key in document):
|
||||
raise ReferenceGraphResultError("reference graph JSON artifact must be an object")
|
||||
return document
|
||||
|
||||
|
||||
def _safe_result_file(root: Path, name: str) -> Path:
|
||||
path = root / name
|
||||
if path.is_symlink():
|
||||
raise ReferenceGraphResultError("reference graph artifact must not be a symlink")
|
||||
resolved = path.resolve(strict=True)
|
||||
if resolved.parent != root or not resolved.is_file():
|
||||
raise ReferenceGraphResultError("reference graph artifact escaped its result")
|
||||
return resolved
|
||||
|
||||
|
||||
def _publish_immutable(staging: Path, target: Path) -> None:
|
||||
if target.exists():
|
||||
if target.is_symlink() or not target.is_dir():
|
||||
@@ -199,5 +318,6 @@ __all__ = [
|
||||
"REFERENCE_GRAPH_RESULT_PREFIX",
|
||||
"ReferenceGraphResultError",
|
||||
"SealedReferenceGraphResult",
|
||||
"read_reference_graph_result",
|
||||
"seal_reference_graph_result",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user