WP-0004: ecosystem integration complete

Add Helix Forge correlation (HELIX_SESSION_UID env, metrics correlate),
artifact-store publish (metrics publish), activity-core ActivityDefinition
references, integration patterns docs, and canon/knowledge design artifacts.
This commit is contained in:
2026-06-16 01:53:01 +02:00
parent 4a9c2d9bea
commit b48a2102d7
22 changed files with 1451 additions and 408 deletions

View File

@@ -11,6 +11,12 @@ from typing import List, Optional
from .registry import AgentRegistry, AgentCategory
from .installer import AgentInstaller, ProjectInitializer, InstallationConfig
from .integrations.artifact_store import (
default_api_token,
default_api_url,
publish_optimizer_evidence,
)
from .integrations.helix import HelixCorrelationAdapter, enrich_helix_correlation
from .metrics import MetricsStore, OptimizerStore, performance_summary_markdown
from .optimization import OptimizationLoop, MIN_SAMPLES_FOR_RECOMMENDATIONS
@@ -999,6 +1005,8 @@ def metrics_record(
if session_id:
payload["session_id"] = session_id
payload = enrich_helix_correlation(payload)
if store.append(payload, idempotency_key=idempotency_key):
click.echo(f"Recorded metrics for '{agent_name}'")
else:
@@ -1106,6 +1114,84 @@ def metrics_optimize(agent_name: Optional[str], target: str, min_samples: int):
click.echo(f"Wrote optimizer analysis: {analysis_path}")
@metrics.command("correlate")
@click.argument("session_uid")
@click.option(
"--store-db",
envvar="HELIX_STORE_DB",
help="Helix Forge session-memory SQLite database path",
)
def metrics_correlate(session_uid: str, store_db: Optional[str]):
"""Look up Helix Forge digest summary for a session UID (read-only)."""
adapter = HelixCorrelationAdapter(
store_db=Path(store_db).resolve() if store_db else None
)
if adapter.store_db is None:
adapter = HelixCorrelationAdapter.from_env()
summary = adapter.lookup(session_uid)
click.echo(json.dumps(summary, indent=2, sort_keys=True))
@metrics.command("publish")
@click.option("--target", "-t", default=".", help="Project root (default: current)")
@click.option(
"--api-url",
default=default_api_url,
show_default=True,
help="artifact-store API base URL (ARTIFACTSTORE_API_URL)",
)
@click.option(
"--token",
default=default_api_token,
help="artifact-store bearer token (ARTIFACTSTORE_API_TOKEN)",
)
@click.option(
"--subject",
help="Package subject (default: project directory name)",
)
@click.option(
"--retention-class",
default="raw-evidence",
show_default=True,
help="artifact-store retention class",
)
def metrics_publish(
target: str,
api_url: str,
token: str,
subject: Optional[str],
retention_class: str,
):
"""Publish optimizer evidence to artifact-store (optional integration)."""
project_root = _project_root(target)
if not token:
click.echo(
"Error: artifact-store token required. Set ARTIFACTSTORE_API_TOKEN or --token.",
err=True,
)
sys.exit(1)
try:
result = publish_optimizer_evidence(
project_root,
api_url=api_url,
token=token,
subject=subject,
retention_class=retention_class,
)
except FileNotFoundError as exc:
click.echo(f"Error: {exc}", err=True)
sys.exit(1)
except RuntimeError as exc:
click.echo(f"Error: {exc}", err=True)
sys.exit(1)
click.echo(f"Published optimizer evidence package: {result.package_id}")
click.echo(f" Files uploaded: {result.files_uploaded}")
click.echo(f" Retention class: {result.retention_class}")
if result.manifest_digest:
click.echo(f" Manifest digest: {result.manifest_digest}")
@metrics.command("export")
@click.argument("agent_name")
@click.option("--target", "-t", default=".", help="Project root (default: current)")

View File

@@ -0,0 +1,10 @@
"""Ecosystem integration adapters (Helix Forge, artifact-store)."""
from .artifact_store import publish_optimizer_evidence
from .helix import HelixCorrelationAdapter, enrich_helix_correlation
__all__ = [
"HelixCorrelationAdapter",
"enrich_helix_correlation",
"publish_optimizer_evidence",
]

View File

@@ -0,0 +1,233 @@
"""artifact-store publish adapter for optimizer evidence (WP-0004 Part 3)."""
from __future__ import annotations
import json
import os
import uuid
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, List, Optional
from urllib import error, parse, request
from ..metrics import OptimizerStore
ENV_API_URL = "ARTIFACTSTORE_API_URL"
ENV_API_TOKEN = "ARTIFACTSTORE_API_TOKEN"
DEFAULT_RETENTION_CLASS = "raw-evidence"
PRODUCER = "kaizen-agentic"
@dataclass
class PublishResult:
package_id: str
manifest_digest: Optional[str]
files_uploaded: int
retention_class: str
def build_optimizer_manifest(
project_root: Path,
*,
agents: Optional[List[str]] = None,
) -> Dict[str, Any]:
"""Manifest metadata for an optimizer evidence package."""
store = OptimizerStore(project_root)
analysis = {}
if store.analysis_path.exists():
analysis = json.loads(store.analysis_path.read_text(encoding="utf-8"))
return {
"schema": "kaizen-agentic/optimizer-evidence/v1",
"project": project_root.name,
"project_root": str(project_root.resolve()),
"producer": PRODUCER,
"retention_class": DEFAULT_RETENTION_CLASS,
"retention_days": 180,
"optimized_at": analysis.get("optimized_at"),
"agents": agents or [item.get("agent") for item in analysis.get("agents", [])],
"files": [
"optimizer/analysis.json",
"optimizer/recommendations.jsonl",
],
}
def publish_optimizer_evidence(
project_root: Path,
*,
api_url: str,
token: str,
subject: Optional[str] = None,
retention_class: str = DEFAULT_RETENTION_CLASS,
) -> PublishResult:
"""Register optimizer outputs as an artifact-store package."""
store = OptimizerStore(project_root)
if not store.analysis_path.exists():
raise FileNotFoundError(
f"No optimizer analysis at {store.analysis_path}. "
"Run: kaizen-agentic metrics optimize"
)
manifest = build_optimizer_manifest(project_root)
package_name = f"kaizen-optimizer-{project_root.name}"
package_subject = subject or project_root.name
created = _http_json(
"POST",
api_url,
"/packages",
token,
{
"name": package_name,
"producer": PRODUCER,
"subject": package_subject,
"retention_class": retention_class,
"metadata": manifest,
},
)
package_id = created["id"]
uploads = [
(
store.analysis_path,
"optimizer/analysis.json",
"application/json",
),
]
if store.recommendations_path.exists():
uploads.append(
(
store.recommendations_path,
"optimizer/recommendations.jsonl",
"application/x-ndjson",
)
)
for path, relative_path, media_type in uploads:
_http_multipart(
api_url,
f"/packages/{package_id}/files",
token,
fields={"relative_path": relative_path, "media_type": media_type},
file_field="file",
file_name=path.name,
file_content_type=media_type,
file_bytes=path.read_bytes(),
)
finalized = _http_json(
"POST",
api_url,
f"/packages/{package_id}/finalize",
token,
{},
)
return PublishResult(
package_id=package_id,
manifest_digest=finalized.get("manifest_digest"),
files_uploaded=len(uploads),
retention_class=retention_class,
)
def default_api_url() -> str:
return os.environ.get(ENV_API_URL, "http://127.0.0.1:8000").rstrip("/")
def default_api_token() -> str:
return os.environ.get(ENV_API_TOKEN, "")
def _http_json(
method: str,
base_url: str,
path: str,
token: str,
payload: Dict[str, Any],
) -> Dict[str, Any]:
body = json.dumps(payload).encode("utf-8") if payload else None
headers = {"Accept": "application/json"}
if body is not None:
headers["Content-Type"] = "application/json"
response = _http_bytes(method, base_url, path, token, body=body, headers=headers)
decoded = json.loads(response)
if not isinstance(decoded, dict):
raise ValueError(f"expected JSON object from {path}")
return decoded
def _http_multipart(
base_url: str,
path: str,
token: str,
*,
fields: Dict[str, str],
file_field: str,
file_name: str,
file_content_type: str,
file_bytes: bytes,
) -> Dict[str, Any]:
boundary = f"kaizen-{uuid.uuid4().hex}"
body = bytearray()
for name, value in fields.items():
body.extend(f"--{boundary}\r\n".encode("ascii"))
body.extend(
f'Content-Disposition: form-data; name="{_quote(name)}"\r\n\r\n'.encode()
)
body.extend(value.encode())
body.extend(b"\r\n")
body.extend(f"--{boundary}\r\n".encode("ascii"))
body.extend(
(
f'Content-Disposition: form-data; name="{_quote(file_field)}"; '
f'filename="{_quote(file_name)}"\r\n'
f"Content-Type: {file_content_type}\r\n\r\n"
).encode()
)
body.extend(file_bytes)
body.extend(b"\r\n")
body.extend(f"--{boundary}--\r\n".encode("ascii"))
response = _http_bytes(
"POST",
base_url,
path,
token,
body=bytes(body),
headers={
"Content-Type": f"multipart/form-data; boundary={boundary}",
"Accept": "application/json",
},
)
decoded = json.loads(response)
if not isinstance(decoded, dict):
raise ValueError(f"expected JSON object from {path}")
return decoded
def _http_bytes(
method: str,
base_url: str,
path: str,
token: str,
*,
body: Optional[bytes] = None,
headers: Optional[Dict[str, str]] = None,
) -> bytes:
url = f"{base_url.rstrip('/')}/{path.lstrip('/')}"
effective_headers = dict(headers or {})
if token:
effective_headers["Authorization"] = f"Bearer {token}"
req = request.Request(url, data=body, headers=effective_headers, method=method)
try:
with request.urlopen(req, timeout=30) as resp:
return resp.read()
except error.HTTPError as exc:
detail = exc.read().decode("utf-8", errors="replace")
raise RuntimeError(f"HTTP {exc.code} from {path}: {detail}") from exc
def _quote(value: str) -> str:
return parse.quote(value, safe="")

View File

@@ -0,0 +1,171 @@
"""Helix Forge correlation adapter (ADR-004, agentic-resources)."""
from __future__ import annotations
import json
import os
import sqlite3
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, Optional
ENV_SESSION_UID = "HELIX_SESSION_UID"
ENV_REPO = "HELIX_REPO"
ENV_FLAVOR = "HELIX_FLAVOR"
ENV_TOKENS = "HELIX_TOKENS"
ENV_INFRA_SHARE = "HELIX_INFRA_OVERHEAD_SHARE"
ENV_STORE_DB = "HELIX_STORE_DB"
def enrich_helix_correlation(record: Dict[str, Any]) -> Dict[str, Any]:
"""Apply optional Helix correlation fields from env or existing record."""
payload = dict(record)
uid = payload.get("helix_session_uid") or os.environ.get(ENV_SESSION_UID)
if uid:
payload["helix_session_uid"] = uid
repo = payload.get("repo") or os.environ.get(ENV_REPO)
if repo:
payload["repo"] = repo
flavor = payload.get("flavor") or os.environ.get(ENV_FLAVOR)
if flavor:
payload["flavor"] = flavor
tokens_raw = payload.get("tokens")
if tokens_raw is None and ENV_TOKENS in os.environ:
try:
tokens_raw = int(os.environ[ENV_TOKENS])
except ValueError:
pass
if tokens_raw is not None:
payload["tokens"] = int(tokens_raw)
infra = payload.get("infra_overhead_share")
if infra is None and ENV_INFRA_SHARE in os.environ:
try:
infra = float(os.environ[ENV_INFRA_SHARE])
except ValueError:
pass
if infra is not None:
payload["infra_overhead_share"] = float(infra)
return payload
def digest_to_correlation_summary(
session_uid: str,
digest: Dict[str, Any],
*,
adapter: str,
) -> Dict[str, Any]:
"""Project a Helix digest into ADR-004 correlation summary fields."""
cost = digest.get("cost") or {}
input_tokens = int(cost.get("input_tokens") or 0)
output_tokens = int(cost.get("output_tokens") or 0)
wall_clock_s = cost.get("wall_clock_s")
summary: Dict[str, Any] = {
"helix_session_uid": session_uid,
"repo": digest.get("repo"),
"flavor": digest.get("flavor"),
"fleet_outcome": digest.get("outcome"),
"tokens": input_tokens + output_tokens,
"adapter": adapter,
}
if wall_clock_s is not None:
summary["wall_clock_s"] = float(wall_clock_s)
markers = digest.get("markers") or {}
tool_histogram = digest.get("tool_histogram") or {}
mcp_calls = sum(
count for tool, count in tool_histogram.items() if str(tool).startswith("mcp__")
)
total_calls = sum(tool_histogram.values()) or 0
if total_calls:
summary["infra_overhead_share"] = round(mcp_calls / total_calls, 3)
elif "infra_overhead_share" in digest:
summary["infra_overhead_share"] = digest["infra_overhead_share"]
if markers:
summary["markers"] = {
key: markers[key]
for key in ("errors", "retries", "test_runs")
if key in markers
}
return summary
@dataclass
class HelixCorrelationAdapter:
"""Read-only lookup of Helix Forge session digests."""
store_db: Optional[Path] = None
@classmethod
def from_env(cls) -> "HelixCorrelationAdapter":
raw = os.environ.get(ENV_STORE_DB)
return cls(store_db=Path(raw).resolve() if raw else None)
def lookup(self, session_uid: str) -> Dict[str, Any]:
if self.store_db and self.store_db.exists():
digest = self._load_digest_sqlite(session_uid)
if digest is not None:
return digest_to_correlation_summary(
session_uid,
digest,
adapter="helix-sqlite",
)
return {
"helix_session_uid": session_uid,
"adapter": "helix-sqlite",
"status": "not_found",
"message": f"No digest for session_uid in {self.store_db}",
}
return {
"helix_session_uid": session_uid,
"adapter": "stub",
"status": "not_configured",
"message": (
"Set HELIX_STORE_DB to an agentic-resources session-memory SQLite "
"database for live lookup. Correlation fields on project metrics "
"still work via HELIX_SESSION_UID at record time."
),
"expected_fields": [
"helix_session_uid",
"repo",
"flavor",
"tokens",
"infra_overhead_share",
"fleet_outcome",
"wall_clock_s",
],
}
def _load_digest_sqlite(self, session_uid: str) -> Optional[Dict[str, Any]]:
conn = sqlite3.connect(str(self.store_db))
try:
row = conn.execute(
"SELECT json FROM digests WHERE session_uid = ?",
(session_uid,),
).fetchone()
if not row:
return None
digest = json.loads(row[0])
digest.setdefault("session_uid", session_uid)
session_row = conn.execute(
"SELECT json FROM sessions WHERE session_uid = ?",
(session_uid,),
).fetchone()
if session_row:
session = json.loads(session_row[0])
digest.setdefault("repo", session.get("repo"))
digest.setdefault("flavor", session.get("flavor"))
return digest
finally:
conn.close()