Harness foundation: INTENT, ADR-001, architecture, prototype adoption
- 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>
This commit is contained in:
parent
87ae78c56a
commit
cfc1b75157
18 changed files with 1204 additions and 1 deletions
88
tests/test_runner.py
Normal file
88
tests/test_runner.py
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from agent_harness.runner import RunResult, run_task
|
||||
from agent_harness.taskspec import TaskSpec, TaskSpecError
|
||||
|
||||
|
||||
def _make_repo(tmp_path: Path) -> Path:
|
||||
repo = tmp_path / "sandbox"
|
||||
repo.mkdir()
|
||||
subprocess.run(["git", "init", "-q"], cwd=repo, check=True)
|
||||
(repo / "README.md").write_text("sandbox\n")
|
||||
subprocess.run(["git", "add", "."], cwd=repo, check=True)
|
||||
subprocess.run(
|
||||
["git", "-c", "user.email=t@t", "-c", "user.name=t", "commit", "-qm", "init"],
|
||||
cwd=repo,
|
||||
check=True,
|
||||
)
|
||||
return repo
|
||||
|
||||
|
||||
class CommittingAdapter:
|
||||
"""Fake adapter that simulates a session which commits."""
|
||||
|
||||
def __init__(self, repo: Path):
|
||||
self.repo = repo
|
||||
self.prompts: list[str] = []
|
||||
|
||||
def execute_prompt(self, prompt, config):
|
||||
self.prompts.append(prompt)
|
||||
(self.repo / "HELLO.md").write_text("hello\n")
|
||||
subprocess.run(["git", "add", "."], cwd=self.repo, check=True)
|
||||
subprocess.run(
|
||||
["git", "-c", "user.email=t@t", "-c", "user.name=t", "commit", "-qm", "task done"],
|
||||
cwd=self.repo,
|
||||
check=True,
|
||||
)
|
||||
from llm_connect.models import LLMResponse
|
||||
|
||||
return LLMResponse(content="done", model="fake", usage={}, finish_reason="stop")
|
||||
|
||||
|
||||
class IdleAdapter:
|
||||
def execute_prompt(self, prompt, config):
|
||||
from llm_connect.models import LLMResponse
|
||||
|
||||
return LLMResponse(content="nothing to do", model="fake", usage={}, finish_reason="stop")
|
||||
|
||||
|
||||
def _spec(repo: Path) -> TaskSpec:
|
||||
return TaskSpec(title="write hello", description="create HELLO.md", target_repo=repo)
|
||||
|
||||
|
||||
def test_run_task_success_when_session_commits(tmp_path) -> None:
|
||||
repo = _make_repo(tmp_path)
|
||||
adapter = CommittingAdapter(repo)
|
||||
|
||||
result = run_task(_spec(repo), adapter=adapter, report_to_hub=False)
|
||||
|
||||
assert isinstance(result, RunResult)
|
||||
assert result.ok is True
|
||||
assert result.committed is True
|
||||
assert result.head_before != result.head_after
|
||||
assert "write hello" in adapter.prompts[0]
|
||||
assert "Never push" in adapter.prompts[0]
|
||||
|
||||
|
||||
def test_run_task_fails_without_commit(tmp_path) -> None:
|
||||
repo = _make_repo(tmp_path)
|
||||
|
||||
result = run_task(_spec(repo), adapter=IdleAdapter(), report_to_hub=False)
|
||||
|
||||
assert result.ok is False
|
||||
assert result.committed is False
|
||||
assert result.reason == "session completed without committing"
|
||||
|
||||
|
||||
def test_taskspec_rejects_non_repo(tmp_path) -> None:
|
||||
spec_file = tmp_path / "task.json"
|
||||
spec_file.write_text(
|
||||
'{"title": "x", "description": "y", "target_repo": "%s"}' % tmp_path
|
||||
)
|
||||
with pytest.raises(TaskSpecError, match="not a git repository"):
|
||||
TaskSpec.from_file(spec_file)
|
||||
Loading…
Add table
Add a link
Reference in a new issue