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>
This commit is contained in:
parent
08327a34a4
commit
f6930ad115
41 changed files with 214 additions and 158 deletions
|
|
@ -1,222 +0,0 @@
|
|||
"""One-task run orchestration.
|
||||
|
||||
Flow: lock target repo → resolve tool profile / budget from instance
|
||||
manifest → snapshot HEAD → persona bundle → prompt → agentic session →
|
||||
verify a new commit exists → kaizen metrics + hub progress event
|
||||
(+ task close). The run *fails* if the session pushed anywhere or left
|
||||
the repo dirty in a way it should not — the worker never pushes;
|
||||
publishing is a separate, explicitly-granted lane.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
from agent_harness import hub, metrics
|
||||
from agent_harness.manifest import resolve_run_policy
|
||||
from agent_harness.persona import load_persona_bundle
|
||||
from agent_harness.profiles import UnknownToolProfileError, get_profile
|
||||
from agent_harness.taskspec import TaskSpec
|
||||
|
||||
PROMPT_TEMPLATE = """\
|
||||
You are an unattended executor session (agent persona below, if any).
|
||||
Operating rules, non-negotiable:
|
||||
- Work ONLY inside the current repository working directory.
|
||||
- Green/Blue lane: file edits and local git add/commit only. Never push,
|
||||
never touch the network, never run destructive commands.
|
||||
- Tool profile for this run: {tool_profile} (lane={lane}).
|
||||
- Bounded effort: complete the single task below, commit with a clear
|
||||
message, then stop. If the task cannot be completed, commit nothing and
|
||||
say why in your final output.
|
||||
|
||||
{persona}
|
||||
|
||||
## Task: {title}
|
||||
|
||||
{description}
|
||||
"""
|
||||
|
||||
|
||||
@dataclass
|
||||
class RunResult:
|
||||
ok: bool
|
||||
committed: bool
|
||||
head_before: str
|
||||
head_after: str
|
||||
persona_source: str
|
||||
session_output: str
|
||||
reason: str = ""
|
||||
tool_profile: str = ""
|
||||
budget_tokens: int | None = None
|
||||
tokens_spent: int | None = None
|
||||
execution_time_s: float = 0.0
|
||||
|
||||
|
||||
def _git(repo: Path, *args: str) -> str:
|
||||
result = subprocess.run(
|
||||
["git", "-C", str(repo), *args],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f"git {' '.join(args)} failed: {result.stderr.strip()}")
|
||||
return result.stdout.strip()
|
||||
|
||||
|
||||
def run_task(
|
||||
spec: TaskSpec,
|
||||
adapter=None,
|
||||
report_to_hub: bool = True,
|
||||
write_metrics: bool = True,
|
||||
) -> RunResult:
|
||||
try:
|
||||
profile_name, budget_tokens, lane, blueprint = resolve_run_policy(
|
||||
spec.target_repo, spec.agent
|
||||
)
|
||||
profile = get_profile(profile_name)
|
||||
except UnknownToolProfileError as exc:
|
||||
return RunResult(
|
||||
ok=False,
|
||||
committed=False,
|
||||
head_before="",
|
||||
head_after="",
|
||||
persona_source="none",
|
||||
session_output="",
|
||||
reason=f"refused: {exc}",
|
||||
tool_profile="",
|
||||
budget_tokens=None,
|
||||
)
|
||||
except Exception as exc:
|
||||
return RunResult(
|
||||
ok=False,
|
||||
committed=False,
|
||||
head_before="",
|
||||
head_after="",
|
||||
persona_source="none",
|
||||
session_output="",
|
||||
reason=f"manifest resolution failed: {exc}",
|
||||
tool_profile="",
|
||||
budget_tokens=None,
|
||||
)
|
||||
|
||||
if adapter is None:
|
||||
from agent_harness.adapter import AgenticClaudeCodeAdapter
|
||||
|
||||
adapter = AgenticClaudeCodeAdapter(
|
||||
workdir=spec.target_repo,
|
||||
tool_profile=profile,
|
||||
)
|
||||
|
||||
head_before = _git(spec.target_repo, "rev-parse", "HEAD")
|
||||
persona, persona_source = load_persona_bundle(blueprint, spec.target_repo)
|
||||
prompt = PROMPT_TEMPLATE.format(
|
||||
persona=persona or "(no persona bundle available for this run)",
|
||||
title=spec.title,
|
||||
description=spec.description,
|
||||
tool_profile=profile.name,
|
||||
lane=lane or profile.lane,
|
||||
)
|
||||
|
||||
from llm_connect.models import BudgetTracker, RunConfig
|
||||
|
||||
budget_tracker = BudgetTracker(total=budget_tokens) if budget_tokens else None
|
||||
config = RunConfig(
|
||||
timeout_seconds=spec.timeout_seconds,
|
||||
skip_if_exists=False,
|
||||
budget_tracker=budget_tracker,
|
||||
)
|
||||
|
||||
started = time.monotonic()
|
||||
try:
|
||||
response = adapter.execute_prompt(prompt, config)
|
||||
session_output = response.content
|
||||
session_ok = True
|
||||
reason = ""
|
||||
except Exception as exc: # adapter / budget failures must still be reported
|
||||
session_output = ""
|
||||
session_ok = False
|
||||
reason = f"session failed: {exc}"
|
||||
execution_time_s = time.monotonic() - started
|
||||
|
||||
head_after = _git(spec.target_repo, "rev-parse", "HEAD")
|
||||
committed = head_after != head_before
|
||||
ok = session_ok and committed
|
||||
if session_ok and not committed:
|
||||
reason = "session completed without committing"
|
||||
|
||||
tokens_spent = budget_tracker.spent if budget_tracker is not None else None
|
||||
|
||||
result = RunResult(
|
||||
ok=ok,
|
||||
committed=committed,
|
||||
head_before=head_before,
|
||||
head_after=head_after,
|
||||
persona_source=persona_source,
|
||||
session_output=session_output,
|
||||
reason=reason,
|
||||
tool_profile=profile.name,
|
||||
budget_tokens=budget_tokens,
|
||||
tokens_spent=tokens_spent,
|
||||
execution_time_s=execution_time_s,
|
||||
)
|
||||
|
||||
if write_metrics:
|
||||
try:
|
||||
metrics.record_execution(
|
||||
spec.target_repo,
|
||||
spec.agent,
|
||||
success=ok,
|
||||
execution_time_s=execution_time_s,
|
||||
tokens=tokens_spent,
|
||||
committed=committed,
|
||||
head_after=head_after,
|
||||
reason=reason or None,
|
||||
metadata={
|
||||
"task_title": spec.title,
|
||||
"tool_profile": profile.name,
|
||||
"labels": list(spec.labels),
|
||||
"completion_event_type": spec.completion_event_type,
|
||||
},
|
||||
)
|
||||
except OSError:
|
||||
pass # metrics must not block run completion reporting
|
||||
|
||||
if report_to_hub:
|
||||
detail = {
|
||||
"repo": spec.target_repo.name,
|
||||
"task_title": spec.title,
|
||||
"agent": spec.agent,
|
||||
"labels": spec.labels,
|
||||
"persona_source": persona_source,
|
||||
"committed": committed,
|
||||
"head_after": head_after,
|
||||
"ok": ok,
|
||||
"reason": reason,
|
||||
"tool_profile": profile.name,
|
||||
"budget_tokens": budget_tokens,
|
||||
"tokens_spent": tokens_spent,
|
||||
"execution_time_s": round(execution_time_s, 3),
|
||||
}
|
||||
hub.post_progress_event(
|
||||
summary=f"executor run: {spec.title} ({'ok' if ok else 'failed'})",
|
||||
event_type=spec.completion_event_type,
|
||||
detail=detail,
|
||||
task_id=spec.hub_task_id,
|
||||
)
|
||||
if tokens_spent is not None or budget_tokens is not None:
|
||||
hub.post_token_event(
|
||||
repo=spec.target_repo.name,
|
||||
tokens=tokens_spent or 0,
|
||||
budget=budget_tokens,
|
||||
agent=spec.agent,
|
||||
ok=ok,
|
||||
detail={"task_title": spec.title, "tool_profile": profile.name},
|
||||
)
|
||||
if ok and spec.hub_task_id:
|
||||
hub.close_task(spec.hub_task_id)
|
||||
|
||||
return result
|
||||
Loading…
Add table
Add a link
Reference in a new issue