init: llm-wiki-haness 하네스 설계
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate a persisted proof manifest reference as a standalone hard gate."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
import proof_manifest
|
||||
|
||||
|
||||
SCHEMA = "proof-hard-gate-result/v1"
|
||||
HEX_SHA256 = re.compile(r"^[0-9a-f]{64}$")
|
||||
|
||||
|
||||
class ProofGateFailure(ValueError):
|
||||
def __init__(self, code: str, message: str) -> None:
|
||||
self.code = code
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
def _allowed_path(path: Path, repo_root: Path, run_root: Path | None) -> Path:
|
||||
resolved = path.resolve(strict=True)
|
||||
allowed = [repo_root, *( [run_root] if run_root is not None else [] )]
|
||||
if not any(resolved.is_relative_to(root) for root in allowed):
|
||||
raise ProofGateFailure("MANIFEST_OUTSIDE_ALLOWED_ROOT", str(resolved))
|
||||
if not resolved.is_file():
|
||||
raise ProofGateFailure("MANIFEST_NOT_FILE", str(resolved))
|
||||
return resolved
|
||||
|
||||
|
||||
def validate_reference(
|
||||
manifest_path: Path,
|
||||
*,
|
||||
expected_sha256: str,
|
||||
expected_proof_count: int,
|
||||
expected_pass_count: int,
|
||||
expected_fail_count: int,
|
||||
repo_root: Path,
|
||||
allowed_profiles: set[str],
|
||||
run_root: Path | None = None,
|
||||
) -> dict[str, Any]:
|
||||
repo_root = repo_root.resolve(strict=True)
|
||||
run_root = run_root.resolve(strict=True) if run_root is not None else None
|
||||
path = _allowed_path(manifest_path, repo_root, run_root)
|
||||
if not HEX_SHA256.fullmatch(expected_sha256):
|
||||
raise ProofGateFailure("INVALID_EXPECTED_MANIFEST_SHA256", expected_sha256)
|
||||
if any(isinstance(value, bool) or not isinstance(value, int) or value < 0 for value in (expected_proof_count, expected_pass_count, expected_fail_count)):
|
||||
raise ProofGateFailure("INVALID_EXPECTED_COUNT", "proof/pass/fail counts must be non-negative integers")
|
||||
content = path.read_bytes()
|
||||
observed_sha256 = hashlib.sha256(content).hexdigest()
|
||||
if observed_sha256 != expected_sha256:
|
||||
raise ProofGateFailure("MANIFEST_HASH_MISMATCH", f"expected {expected_sha256}, observed {observed_sha256}")
|
||||
manifest = json.loads(content.decode("utf-8"))
|
||||
if not isinstance(manifest, dict) or manifest.get("schema_version") != proof_manifest.SCHEMA_VERSION:
|
||||
raise ProofGateFailure("MANIFEST_SCHEMA_MISMATCH", f"expected {proof_manifest.SCHEMA_VERSION}")
|
||||
verified = proof_manifest.verify_manifest(manifest, repo_root, allowed_profiles, run_root=run_root)
|
||||
verification = verified["verification"]
|
||||
observed = (
|
||||
verification["proof_count"],
|
||||
verification["pass_count"],
|
||||
verification["fail_count"],
|
||||
)
|
||||
expected = (expected_proof_count, expected_pass_count, expected_fail_count)
|
||||
if observed != expected:
|
||||
raise ProofGateFailure("MANIFEST_COUNT_MISMATCH", f"expected {expected}, observed {observed}")
|
||||
if verification.get("status") != "PASS" or expected_fail_count != 0 or expected_pass_count != expected_proof_count:
|
||||
raise ProofGateFailure("PROOF_GATE_NOT_PASS", f"verification={verification}")
|
||||
return {
|
||||
"schema_version": SCHEMA,
|
||||
"status": "PASS",
|
||||
"manifest": {"path": path.as_posix(), "sha256": observed_sha256, "schema_version": manifest["schema_version"]},
|
||||
"proof_count": observed[0],
|
||||
"pass_count": observed[1],
|
||||
"fail_count": observed[2],
|
||||
}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("manifest", type=Path)
|
||||
parser.add_argument("--manifest-sha256", required=True)
|
||||
parser.add_argument("--proof-count", required=True, type=int)
|
||||
parser.add_argument("--pass-count", required=True, type=int)
|
||||
parser.add_argument("--fail-count", required=True, type=int)
|
||||
parser.add_argument("--repo-root", type=Path, default=proof_manifest.DEFAULT_REPO_ROOT)
|
||||
parser.add_argument("--run-root", type=Path)
|
||||
parser.add_argument("--profiles", type=Path, default=proof_manifest.DEFAULT_PROFILES)
|
||||
args = parser.parse_args(argv)
|
||||
try:
|
||||
repo_root = args.repo_root.resolve(strict=True)
|
||||
run_root = args.run_root.resolve(strict=True) if args.run_root is not None else None
|
||||
profiles = proof_manifest.load_allowed_profiles(args.profiles.resolve(strict=True))
|
||||
result = validate_reference(
|
||||
args.manifest,
|
||||
expected_sha256=args.manifest_sha256,
|
||||
expected_proof_count=args.proof_count,
|
||||
expected_pass_count=args.pass_count,
|
||||
expected_fail_count=args.fail_count,
|
||||
repo_root=repo_root,
|
||||
run_root=run_root,
|
||||
allowed_profiles=profiles,
|
||||
)
|
||||
json.dump(result, sys.stdout, ensure_ascii=False, indent=2, sort_keys=True)
|
||||
sys.stdout.write("\n")
|
||||
return 0
|
||||
except (ProofGateFailure, proof_manifest.ManifestValidationError) as exc:
|
||||
errors = exc.issues if isinstance(exc, proof_manifest.ManifestValidationError) else [{"code": exc.code, "message": str(exc)}]
|
||||
json.dump({"schema_version": SCHEMA, "status": "FAIL", "errors": errors}, sys.stdout, ensure_ascii=False, indent=2, sort_keys=True)
|
||||
sys.stdout.write("\n")
|
||||
return 1
|
||||
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
|
||||
json.dump({"schema_version": SCHEMA, "status": "ERROR", "errors": [{"code": "PROOF_GATE_ERROR", "message": str(exc)}]}, sys.stdout, ensure_ascii=False, indent=2, sort_keys=True)
|
||||
sys.stdout.write("\n")
|
||||
return 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user