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>
48 lines
1.6 KiB
Python
48 lines
1.6 KiB
Python
"""Task specification the worker consumes.
|
|
|
|
MVP source is a JSON file matching what the activity-core issue-core sink
|
|
emits per rule action (task_template/description/target_repo/labels), plus
|
|
executor-specific fields. A future source polls issue-core directly.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from dataclasses import dataclass, field
|
|
from pathlib import Path
|
|
|
|
|
|
class TaskSpecError(ValueError):
|
|
pass
|
|
|
|
|
|
@dataclass
|
|
class TaskSpec:
|
|
title: str
|
|
description: str
|
|
target_repo: Path
|
|
agent: str = "coach"
|
|
labels: list[str] = field(default_factory=list)
|
|
hub_task_id: str | None = None
|
|
completion_event_type: str = "executor_run"
|
|
timeout_seconds: int = 900
|
|
|
|
@classmethod
|
|
def from_file(cls, path: str | Path) -> "TaskSpec":
|
|
raw = json.loads(Path(path).read_text())
|
|
missing = {"title", "description", "target_repo"} - set(raw)
|
|
if missing:
|
|
raise TaskSpecError(f"task spec missing field(s): {', '.join(sorted(missing))}")
|
|
target = Path(raw["target_repo"]).expanduser()
|
|
if not (target / ".git").is_dir():
|
|
raise TaskSpecError(f"target_repo is not a git repository: {target}")
|
|
return cls(
|
|
title=str(raw["title"]),
|
|
description=str(raw["description"]),
|
|
target_repo=target,
|
|
agent=str(raw.get("agent", "coach")),
|
|
labels=[str(label) for label in raw.get("labels", [])],
|
|
hub_task_id=raw.get("hub_task_id"),
|
|
completion_event_type=str(raw.get("completion_event_type", "executor_run")),
|
|
timeout_seconds=int(raw.get("timeout_seconds", 900)),
|
|
)
|