- INTENT.md: three-layer model (blueprint/instance/harness), single shared runtime, never-become boundaries - ADR-001 (accepted): DEC-2026-002 resolution — one harness repo for all projects; instances are declarative state in consuming repos - docs/architecture.md: components, contracts (manifest, tool profiles, completion events, credential lanes), deployment shape - agent_harness/: executor-worker prototype adopted and renamed (6/6 tests green); HARNESS-WP-0001 initial workplan (7 tasks) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
126 lines
3.8 KiB
Python
126 lines
3.8 KiB
Python
"""One-task run orchestration.
|
|
|
|
Flow: lock target repo → snapshot HEAD → persona bundle → prompt → agentic
|
|
session → verify a new commit exists → 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 (see integrations/executor-worker-secrets.md in
|
|
binky-control).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
|
|
from agent_harness import hub
|
|
from agent_harness.persona import load_persona_bundle
|
|
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.
|
|
- 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 = ""
|
|
|
|
|
|
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) -> RunResult:
|
|
if adapter is None:
|
|
from agent_harness.adapter import AgenticClaudeCodeAdapter
|
|
|
|
adapter = AgenticClaudeCodeAdapter(workdir=spec.target_repo)
|
|
|
|
head_before = _git(spec.target_repo, "rev-parse", "HEAD")
|
|
persona, persona_source = load_persona_bundle(spec.agent, spec.target_repo)
|
|
prompt = PROMPT_TEMPLATE.format(
|
|
persona=persona or "(no persona bundle available for this run)",
|
|
title=spec.title,
|
|
description=spec.description,
|
|
)
|
|
|
|
from llm_connect.models import RunConfig
|
|
|
|
config = RunConfig(timeout_seconds=spec.timeout_seconds, skip_if_exists=False)
|
|
try:
|
|
response = adapter.execute_prompt(prompt, config)
|
|
session_output = response.content
|
|
session_ok = True
|
|
reason = ""
|
|
except Exception as exc: # adapter failures must still be reported
|
|
session_output = ""
|
|
session_ok = False
|
|
reason = f"session failed: {exc}"
|
|
|
|
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"
|
|
|
|
result = RunResult(
|
|
ok=ok,
|
|
committed=committed,
|
|
head_before=head_before,
|
|
head_after=head_after,
|
|
persona_source=persona_source,
|
|
session_output=session_output,
|
|
reason=reason,
|
|
)
|
|
|
|
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,
|
|
}
|
|
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 ok and spec.hub_task_id:
|
|
hub.close_task(spec.hub_task_id)
|
|
|
|
return result
|