feat(STATE-WP-0079): add repo-manager receiving adapters
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 23s

This commit is contained in:
tegwick 2026-08-21 17:15:28 +02:00
parent 87f1296714
commit de58a0cf90
2 changed files with 255 additions and 0 deletions

View file

@ -249,6 +249,166 @@ def rm_update_task_status(
return result
def rm_update_workplan(
*,
repo_path: str | Path,
workplan_id: str,
operation: str = "update",
title: str | None = None,
goal: str | None = None,
status: str | None = None,
owner: str | None = None,
domain: str | None = None,
topic_slug: str | None = None,
repo_slug: str | None = None,
reason: str = "state-hub dual-run",
push: bool | None = None,
correlation_id: str | None = None,
confirm_archive: bool = False,
) -> dict[str, Any]:
"""Delegate a file-backed workplan mutation to Repo Manager.
``operation=archive`` is the recoverable counterpart of State Hub's
DELETE workplan route; it never erases repository history.
"""
correlation_id = correlation_id or str(uuid.uuid4())
if operation not in {"create", "update", "archive"}:
return {
"status": "rejected",
"correlation_id": correlation_id,
"error": {"code": "validation_error", "message": f"invalid operation {operation!r}"},
}
if push is None:
push = writeback_push_enabled()
cli_operation = "delete" if operation == "archive" else operation
args = [
"workplan",
cli_operation,
"--path",
str(repo_path),
"--workplan-id",
str(workplan_id),
"--reason",
reason,
"--correlation-id",
correlation_id,
"--idempotency-key",
f"sh-dual-workplan-{operation}-{workplan_id}-{correlation_id}",
]
for flag, value in (
("--title", title),
("--goal", goal),
("--status", status),
("--owner", owner),
("--domain", domain),
("--topic-slug", topic_slug),
("--slug", repo_slug),
):
if value is not None:
args.extend([flag, value])
if operation == "archive" and confirm_archive:
args.append("--confirm")
if push:
args.append("--push")
code, out, err = run_rmgr(args)
try:
result = json.loads(out.strip() or "{}")
except json.JSONDecodeError:
result = {
"status": "failed",
"error": {
"code": "internal",
"message": f"rmgr non-json exit={code} stderr={err!r} stdout={out[:500]!r}",
},
"correlation_id": correlation_id,
}
if code != 0 and result.get("status") not in ("applied", "rejected"):
result.setdefault("status", "failed")
result.setdefault(
"error",
{"code": "internal", "message": f"rmgr exit={code} stderr={err!r}"},
)
return result
def rm_update_register_entry(
*,
repo_path: str | Path,
kind: str,
entry_id: str,
operation: str = "put",
title: str | None = None,
status: str | None = None,
data: dict[str, Any] | None = None,
note: str | None = None,
author: str | None = None,
repo_slug: str | None = None,
reason: str = "state-hub retirement adapter",
push: bool | None = None,
correlation_id: str | None = None,
) -> dict[str, Any]:
"""Delegate register create/update/defer/note to Repo Manager."""
correlation_id = correlation_id or str(uuid.uuid4())
if operation not in {"put", "defer", "note"}:
return {
"status": "rejected",
"correlation_id": correlation_id,
"error": {"code": "validation_error", "message": f"invalid operation {operation!r}"},
}
if push is None:
push = writeback_push_enabled()
args = [
"register",
operation,
"--path",
str(repo_path),
"--kind",
kind,
"--entry-id",
entry_id,
"--reason",
reason,
"--correlation-id",
correlation_id,
"--idempotency-key",
f"sh-dual-register-{operation}-{kind}-{entry_id}-{correlation_id}",
]
for flag, value in (
("--title", title),
("--status", status),
("--note", note),
("--author", author),
("--slug", repo_slug),
):
if value is not None:
args.extend([flag, value])
if data is not None:
args.extend(["--data-json", json.dumps(data, separators=(",", ":"))])
if push:
args.append("--push")
code, out, err = run_rmgr(args)
try:
result = json.loads(out.strip() or "{}")
except json.JSONDecodeError:
result = {
"status": "failed",
"error": {
"code": "internal",
"message": f"rmgr non-json exit={code} stderr={err!r} stdout={out[:500]!r}",
},
"correlation_id": correlation_id,
}
if code != 0 and result.get("status") not in ("applied", "rejected"):
result.setdefault("status", "failed")
result.setdefault(
"error",
{"code": "internal", "message": f"rmgr exit={code} stderr={err!r}"},
)
return result
def rm_scaffold(
*,
repo_path: str | Path,

View file

@ -0,0 +1,95 @@
"""Compatibility adapter coverage for RMGR-WP-0008-T01."""
from api.services import repo_manager_dual_run as adapter
def test_rm_update_workplan_builds_governed_cli_command(monkeypatch):
seen = {}
def fake_run(args, *, timeout=120):
seen["args"] = args
return 0, '{"status":"applied","evidence":{"git_sha":"abc"}}', ""
monkeypatch.setattr(adapter, "run_rmgr", fake_run)
result = adapter.rm_update_workplan(
repo_path="/repos/demo",
workplan_id="DEMO-WP-0001",
operation="update",
title="Renamed",
status="active",
repo_slug="demo",
correlation_id="00000000-0000-4000-8000-000000000001",
push=True,
)
assert result["status"] == "applied"
assert seen["args"][:5] == [
"workplan",
"update",
"--path",
"/repos/demo",
"--workplan-id",
]
assert "--title" in seen["args"]
assert "--status" in seen["args"]
assert "--push" in seen["args"]
def test_rm_update_workplan_maps_delete_to_confirmed_archive(monkeypatch):
seen = {}
def fake_run(args, *, timeout=120):
seen["args"] = args
return 0, '{"status":"applied"}', ""
monkeypatch.setattr(adapter, "run_rmgr", fake_run)
result = adapter.rm_update_workplan(
repo_path="/repos/demo",
workplan_id="DEMO-WP-0001",
operation="archive",
confirm_archive=True,
push=False,
)
assert result["status"] == "applied"
assert seen["args"][0:2] == ["workplan", "delete"]
assert "--confirm" in seen["args"]
assert "--push" not in seen["args"]
def test_rm_update_workplan_rejects_unknown_operation_without_invocation(monkeypatch):
def fail_run(*args, **kwargs):
raise AssertionError("rmgr must not run")
monkeypatch.setattr(adapter, "run_rmgr", fail_run)
result = adapter.rm_update_workplan(
repo_path="/repos/demo",
workplan_id="DEMO-WP-0001",
operation="erase",
)
assert result["status"] == "rejected"
assert result["error"]["code"] == "validation_error"
def test_rm_update_register_entry_builds_shared_spine_command(monkeypatch):
seen = {}
def fake_run(args, *, timeout=120):
seen["args"] = args
return 0, '{"status":"applied"}', ""
monkeypatch.setattr(adapter, "run_rmgr", fake_run)
result = adapter.rm_update_register_entry(
repo_path="/repos/demo",
kind="technical-debt",
entry_id="TD-001",
operation="put",
title="Debt",
data={"severity": "high"},
push=False,
)
assert result["status"] == "applied"
assert seen["args"][0:2] == ["register", "put"]
assert "technical-debt" in seen["args"]
assert '{"severity":"high"}' in seen["args"]