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>
2026-07-17 23:35:27 +02:00
|
|
|
"""Custodian State Hub reporting (REST, no MCP).
|
|
|
|
|
|
|
|
|
|
STATE_HUB_URL defaults to the workstation-local hub; on Railiance the
|
|
|
|
|
ops-bridge tunnel exposes it at http://127.0.0.1:18000.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
from typing import Any
|
|
|
|
|
|
|
|
|
|
import httpx
|
|
|
|
|
|
|
|
|
|
_DEFAULT_URL = "http://127.0.0.1:8000"
|
|
|
|
|
_TIMEOUT = 10.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _base_url() -> str:
|
|
|
|
|
return os.environ.get("STATE_HUB_URL", _DEFAULT_URL).rstrip("/")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def post_progress_event(
|
|
|
|
|
summary: str,
|
|
|
|
|
event_type: str,
|
|
|
|
|
detail: dict[str, Any],
|
|
|
|
|
task_id: str | None = None,
|
|
|
|
|
) -> bool:
|
|
|
|
|
payload: dict[str, Any] = {
|
|
|
|
|
"summary": summary,
|
|
|
|
|
"event_type": event_type,
|
|
|
|
|
"detail": detail,
|
|
|
|
|
"author": "agt-executor-worker",
|
|
|
|
|
}
|
|
|
|
|
if task_id:
|
|
|
|
|
payload["task_id"] = task_id
|
|
|
|
|
try:
|
|
|
|
|
resp = httpx.post(f"{_base_url()}/progress/", json=payload, timeout=_TIMEOUT)
|
|
|
|
|
resp.raise_for_status()
|
|
|
|
|
return True
|
|
|
|
|
except httpx.HTTPError:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def close_task(task_id: str) -> bool:
|
|
|
|
|
try:
|
|
|
|
|
resp = httpx.patch(
|
|
|
|
|
f"{_base_url()}/tasks/{task_id}",
|
|
|
|
|
json={"status": "done"},
|
|
|
|
|
timeout=_TIMEOUT,
|
|
|
|
|
)
|
|
|
|
|
resp.raise_for_status()
|
|
|
|
|
return True
|
|
|
|
|
except httpx.HTTPError:
|
|
|
|
|
return False
|
2026-07-17 23:49:03 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def post_token_event(
|
|
|
|
|
repo: str,
|
|
|
|
|
tokens: int,
|
|
|
|
|
*,
|
|
|
|
|
budget: int | None = None,
|
|
|
|
|
agent: str | None = None,
|
|
|
|
|
ok: bool = True,
|
|
|
|
|
detail: dict[str, Any] | None = None,
|
|
|
|
|
) -> bool:
|
|
|
|
|
"""Best-effort token cost event for the hub Token Cost dashboard.
|
|
|
|
|
|
|
|
|
|
Schema is intentionally loose: if the hub rejects the payload we
|
|
|
|
|
swallow the error so a metrics-schema drift never fails a run.
|
|
|
|
|
"""
|
|
|
|
|
payload: dict[str, Any] = {
|
|
|
|
|
"repo": repo,
|
|
|
|
|
"tokens": tokens,
|
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
|
|
|
"source": "rein-aharness",
|
2026-07-17 23:49:03 +02:00
|
|
|
"ok": ok,
|
|
|
|
|
}
|
|
|
|
|
if budget is not None:
|
|
|
|
|
payload["budget"] = budget
|
|
|
|
|
if agent is not None:
|
|
|
|
|
payload["agent"] = agent
|
|
|
|
|
if detail:
|
|
|
|
|
payload["detail"] = detail
|
|
|
|
|
try:
|
|
|
|
|
resp = httpx.post(
|
|
|
|
|
f"{_base_url()}/token-events/upsert",
|
|
|
|
|
json=payload,
|
|
|
|
|
timeout=_TIMEOUT,
|
|
|
|
|
)
|
|
|
|
|
if resp.status_code >= 400:
|
|
|
|
|
# Fallback shape used by some hub builds
|
|
|
|
|
resp = httpx.post(
|
|
|
|
|
f"{_base_url()}/token-events/",
|
|
|
|
|
json=payload,
|
|
|
|
|
timeout=_TIMEOUT,
|
|
|
|
|
)
|
|
|
|
|
resp.raise_for_status()
|
|
|
|
|
return True
|
|
|
|
|
except httpx.HTTPError:
|
|
|
|
|
return False
|