test: prepare real local rein acceptance with artifact verification
All checks were successful
ci / validate (push) Successful in 2m51s
All checks were successful
ci / validate (push) Successful in 2m51s
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0726e-5232-73f2-aaca-2c05ceb62efb
This commit is contained in:
parent
97daba45dd
commit
ce37e1cb63
6 changed files with 451 additions and 2 deletions
212
scripts/prove-local-profile.py
Normal file
212
scripts/prove-local-profile.py
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
"""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())
|
||||
Loading…
Add table
Add a link
Reference in a new issue