glas-harness/scripts/prove-local-profile.py
tegwick ce37e1cb63
All checks were successful
ci / validate (push) Successful in 2m51s
test: prepare real local rein acceptance with artifact verification
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0726e-5232-73f2-aaca-2c05ceb62efb
2026-09-05 19:35:18 +02:00

212 lines
9.7 KiB
Python

"""Real-rein acceptance fixture for GLAS-WP-0012; never overrides readiness."""
import argparse
import json
import subprocess
import tempfile
from pathlib import Path
from sandboxer.core.manager import SandboxManager
from sandboxer.lifecycle.store import SandboxStore
from sandboxer.payments.credits import CreditsStore
from sandboxer.snapshots.store import SnapshotStore
from glas_harness.contract import ExecutionRequest, Rein
from glas_harness.gateway import run_execution
from glas_harness.profiles import ProfileCatalog
from glas_harness.reins.rein_aharness import ReinAharness
# Executed inside the sandbox; results contain only booleans and a Git object id.
INSPECTOR = r'''
import json, os, stat, subprocess, sys
from pathlib import Path
baseline, source, task, request_id = sys.argv[1:]
def git(*args):
return subprocess.check_output(['git', *args], stderr=subprocess.DEVNULL,
timeout=10)
head = git('rev-parse', 'HEAD').decode().strip()
artifact = Path('PROOF.md')
expected = b'Glas local profile proof.\n'
checks = {
'one_commit': git('rev-list', '--parents', '-n', '1', 'HEAD').decode().split()
== [head, baseline],
'only_expected_path': git('diff-tree', '--no-commit-id', '--name-only', '-r',
'-z', 'HEAD') == b'PROOF.md\x00',
'clean_worktree': not git('status', '--porcelain', '--untracked-files=all',
'--ignored=matching').strip(),
'regular_artifact': artifact.is_file() and not artifact.is_symlink(),
'worktree_content': artifact.is_file() and artifact.read_bytes() == expected,
'committed_content': git('show', 'HEAD:PROOF.md') == expected,
'source_absent': not Path(source).exists(),
'task_private': stat.S_IMODE(Path(task).stat().st_mode) == 0o600,
'identity_exact': os.environ.get('SANDBOXER_ACTOR') == 'agt'
and os.environ.get('SANDBOXER_PROJECT') == 'glas-local-proof'
and os.environ.get('SANDBOXER_RUN_ID') == request_id,
}
print(json.dumps({'head': head, 'checks': checks}))
'''
CHECKS = {
"one_commit", "only_expected_path", "clean_worktree", "regular_artifact",
"worktree_content", "committed_content", "source_absent", "task_private",
"identity_exact",
}
class ObservedRein(Rein):
"""Observe the catalog-built adapter without replacing its agent dispatch."""
def __init__(self, delegate, source):
self.delegate = delegate
self.source = source
self.workspace = None
self.request_id = None
self.checks = {}
self.task_removed = False
def start_session(self, profile, inputs, sandbox):
reachability = sandbox.reachability
if not reachability.get("workspace_dir") or reachability.get("ssh"):
raise RuntimeError("local proof requires an owner-managed local workspace")
self.workspace = reachability["workspace_dir"]
self.request_id = inputs["request_id"]
return self.delegate.start_session(profile, inputs, sandbox)
def dispatch_tool(self, session, tool_call):
return self.delegate.dispatch_tool(session, tool_call)
def end_session(self, session):
summary = self.delegate.end_session(session)
if summary.outcome != "succeeded":
return summary
result = session["transport"].run(
["/usr/bin/python3", "-c", INSPECTOR, session["head_before"],
str(self.source), session["task_file"], self.request_id], timeout=30,
)
if result.returncode:
raise RuntimeError("sandbox artifact inspection failed")
try:
report = json.loads(result.stdout)
checks = report["checks"]
self.checks = {key: checks.get(key) is True for key in CHECKS}
self.checks["head_matches_summary"] = report["head"] == summary.commit_sha
except (ValueError, KeyError, TypeError, AttributeError) as exc:
raise RuntimeError("invalid sandbox artifact inspection") from exc
if not all(self.checks.values()):
raise RuntimeError("sandbox artifact acceptance failed")
return summary
def cleanup_session(self, session):
self.delegate.cleanup_session(session)
proc = session["transport"].run(
["test", "!", "-e", session["task_file"]], timeout=15
)
self.task_removed = proc.returncode == 0
if not self.task_removed:
raise RuntimeError("generated task file survived cleanup")
def select_candidate(reference, catalog):
if "@" not in reference:
raise ValueError("proof requires an exact version-pinned profile")
profile, descriptor = catalog.resolve(reference)
catalog.require_operational(profile)
if (profile.id != "harness.agent-dev-local" or descriptor.id != "rein-aharness"
or profile.model.route != "claude-code-cli"
or profile.model.provider != "anthropic"):
raise ValueError("proof requires the reviewed local Claude route")
if not profile.credential_route_refs:
raise ValueError("proof requires declared credential route references")
if (not profile.limits.timeout_seconds or profile.limits.timeout_seconds > 900
or not profile.limits.budget_tokens or profile.limits.budget_tokens > 60000):
raise ValueError("proof requires explicit bounded token and time limits")
delegate = catalog.build_rein(profile, descriptor)
if type(delegate) is not ReinAharness:
raise ValueError("proof requires the actual ReinAharness adapter")
return profile, delegate
def git(source, *args):
return subprocess.check_output(["git", *args], cwd=source,
stderr=subprocess.DEVNULL, timeout=15)
def prove(reference, catalog):
# This gate precedes even the disposable fixture and sandbox manager.
profile, delegate = select_candidate(reference, catalog)
with tempfile.TemporaryDirectory(prefix="glas-real-rein-proof-") as directory:
root = Path(directory)
source = root / "source"
source.mkdir()
git(source, "init", "-q")
git(source, "config", "user.name", "Glas Acceptance")
git(source, "config", "user.email", "proof@example.invalid")
(source / "sentinel").write_bytes(b"unchanged\n")
git(source, "add", "sentinel")
git(source, "commit", "-qm", "fixture")
source_head = git(source, "rev-parse", "HEAD")
manager = SandboxManager(
store=SandboxStore(path=root / "sandboxes.json"),
credits=CreditsStore(path=root / "credits.json"),
snapshots=SnapshotStore(path=root / "snapshots.json"),
)
observed = ObservedRein(delegate, source)
result = run_execution(ExecutionRequest(
harness_profile_ref=reference, repo=str(source), title="local profile proof",
description=("Create only PROOF.md containing exactly 'Glas local profile proof.' "
"followed by one newline. Make exactly one local commit. "
"Leave the working tree clean. Do not push or change other files."),
actor="agt", project="glas-local-proof", report_to_hub=False,
), catalog=catalog, rein=observed, manager=manager)
source_unchanged = (
(source / "sentinel").read_bytes() == b"unchanged\n"
and git(source, "rev-parse", "HEAD") == source_head
and not git(source, "status", "--porcelain", "--untracked-files=all",
"--ignored=matching").strip()
)
status = manager.store.get(result.evidence.sandbox_id) if result.evidence.sandbox_id else None
destroyed = status is not None and status.state.value == "destroyed"
workspace_removed = bool(observed.workspace) and not Path(observed.workspace).exists()
passed = (result.ok and bool(observed.checks) and all(observed.checks.values())
and observed.task_removed and source_unchanged and destroyed
and workspace_removed)
# Do not print tool_output, tool_error, exceptions, prompts, or raw owner stdout.
return {
"acceptance_passed": passed, "profile_ref": reference,
"profile_readiness": profile.operational_readiness.status,
"sandbox_profile": profile.sandbox_profile,
"requested_model": profile.model.model,
"model_route": profile.model.route,
"credential_route_refs": profile.credential_route_refs,
"limits": profile.limits.model_dump(exclude_none=True),
"request_id": result.evidence.request_id,
"sandbox_id": result.evidence.sandbox_id,
"commit_sha": result.evidence.commit_sha,
"outcome": result.evidence.outcome,
"failure_stage": result.evidence.failure_stage,
"artifact_checks": observed.checks,
"source_unchanged": source_unchanged,
"task_removed_before_teardown": observed.task_removed,
"destroyed": destroyed, "workspace_removed": workspace_removed,
"readiness_review_required": True,
}
def main(argv=None):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--harness-profile", required=True)
parser.add_argument("--profile-dir", type=Path)
args = parser.parse_args(argv)
try:
report = prove(args.harness_profile, ProfileCatalog(profile_dir=args.profile_dir))
except Exception as exc:
# Configuration/provider errors can contain secret values; expose only the type.
report = {"acceptance_passed": False, "error_type": type(exc).__name__,
"readiness_review_required": True}
print(json.dumps(report, indent=2))
return 0 if report["acceptance_passed"] else 1
if __name__ == "__main__":
raise SystemExit(main())