rein-aharness/scripts/tenant_onboard_runs.py
tegwick f6930ad115 Rename package, CLI, and deploy artifacts to rein-aharness (HARNESS-WP-0002-T02)
agent_harness -> rein_aharness (package + all imports), CLI command
agent-harness -> rein-aharness, Docker image tag, k8s namespace/labels/
names, Makefile targets, deploy script env var/paths. In-repo identity
strings (hub event source, metrics harness field, default assignee,
argparse prog name, commit author identity) updated to match.

Historical documents left untouched on purpose: docs/adr/ADR-001-agent-harness-architecture.md,
docs/architecture.md (dated v0.1 snapshot), workplans/HARNESS-WP-0001
(completed under the old name), and the SSH host alias
"forgejo-agent-harness" (external ~/.ssh/config entry, not owned here).

Verified: 47/47 tests pass, CLI runs correctly from a fresh venv,
`make image` builds and the resulting container runs correctly.

deploy/README.md gained an explicit rename cutover checklist for what
this session cannot safely do unattended -- moving the host-side
secrets dir and checkout on railiance01, and not deleting the old k8s
namespace until the new one is confirmed working. The actual live
cutover (running that checklist against the real Railiance deployment)
is not attempted here -- real production surgery on binky-control's
live automation, needs the operator present.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 14:22:18 +02:00

111 lines
3.5 KiB
Python

#!/usr/bin/env python3
"""Run tenant onboarding task specs through the harness (deterministic adapter).
Used for HARNESS-WP-0001-T07 when proving instance entries + hub completion
events without a full Claude Code session. Each run commits a small evidence
file under history/harness-onboarding/ and posts the task's completion_event_type.
"""
from __future__ import annotations
import argparse
import json
import subprocess
import sys
from pathlib import Path
# Allow running from repo root without install.
_ROOT = Path(__file__).resolve().parents[1]
if str(_ROOT) not in sys.path:
sys.path.insert(0, str(_ROOT))
from rein_aharness.runner import run_task
from rein_aharness.taskspec import TaskSpec
class EvidenceAdapter:
def __init__(self, repo: Path, slug: str, event_type: str):
self.repo = repo
self.slug = slug
self.event_type = event_type
self.prompts: list[str] = []
def execute_prompt(self, prompt, config):
self.prompts.append(prompt)
out_dir = self.repo / "history" / "harness-onboarding"
out_dir.mkdir(parents=True, exist_ok=True)
path = out_dir / f"{self.slug}.md"
path.write_text(
f"# Harness onboarding run — {self.slug}\n\n"
f"- completion_event_type: `{self.event_type}`\n"
f"- tool profile resolved from instance manifest\n"
f"- deterministic adapter (T07 proof; not a full agentic session)\n",
encoding="utf-8",
)
subprocess.run(["git", "add", str(path.relative_to(self.repo))], cwd=self.repo, check=True)
subprocess.run(
[
"git",
"-c",
"user.email=rein-aharness@local",
"-c",
"user.name=rein-aharness",
"commit",
"-qm",
f"harness onboard: {self.slug}",
],
cwd=self.repo,
check=True,
)
from llm_connect.models import LLMResponse
return LLMResponse(
content=f"onboard {self.slug} ok",
model="onboard-adapter",
usage={},
finish_reason="stop",
)
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--task-file",
action="append",
required=True,
help="Task JSON path (repeatable)",
)
parser.add_argument("--no-hub", action="store_true")
args = parser.parse_args(argv)
results = []
for task_path in args.task_file:
spec = TaskSpec.from_file(task_path)
slug = Path(task_path).stem
adapter = EvidenceAdapter(spec.target_repo, slug, spec.completion_event_type)
result = run_task(
spec,
adapter=adapter,
report_to_hub=not args.no_hub,
write_metrics=True,
)
results.append(
{
"task_file": task_path,
"ok": result.ok,
"committed": result.committed,
"tool_profile": result.tool_profile,
"event": spec.completion_event_type,
"reason": result.reason,
"head_after": result.head_after,
}
)
print(json.dumps(results[-1], indent=2))
if not result.ok:
return 1
print(json.dumps({"all_ok": True, "runs": len(results)}, indent=2))
return 0
if __name__ == "__main__":
raise SystemExit(main())