Prepare State Hub retirement baseline
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 1m0s

This commit is contained in:
tegwick 2026-08-09 16:19:53 +02:00
parent 2217bdd9f5
commit 5927591be8
46 changed files with 32583 additions and 62 deletions

View file

@ -0,0 +1,40 @@
import os
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
LAUNCHER = ROOT / "scripts" / "codex-state-hub-mcp.sh"
def test_codex_mcp_launcher_uses_configured_python(tmp_path: Path) -> None:
fake_python = tmp_path / "python"
fake_python.write_text(
"#!/usr/bin/env bash\n"
"printf 'python=%s\\nscript=%s\\ntransport=%s\\n' "
'"$0" "$1" "$MCP_TRANSPORT"\n'
)
fake_python.chmod(0o755)
env = os.environ.copy()
env["STATE_HUB_PYTHON"] = str(fake_python)
env["API_BASE"] = "http://127.0.0.1:8000"
result = subprocess.run(
[str(LAUNCHER)],
cwd=ROOT,
env=env,
text=True,
capture_output=True,
check=True,
timeout=5,
)
assert f"python={fake_python}" in result.stdout
assert "script=mcp_server/codex_server.py" in result.stdout
assert "transport=stdio" in result.stdout
def test_codex_mcp_launcher_has_no_uv_runtime_dependency() -> None:
launcher = LAUNCHER.read_text()
assert "uv run" not in launcher
assert ".venv/bin/python" in launcher

View file

@ -0,0 +1,19 @@
import asyncio
from mcp_server import codex_server
def test_codex_server_has_only_repository_coordination_tools() -> None:
tools = asyncio.run(codex_server.mcp.list_tools())
assert {tool.name for tool in tools} == {
"get_domain_summary",
"get_messages",
"mark_message_read",
"add_progress_event",
"record_decision",
"update_task_status",
}
def test_codex_server_uses_distinct_server_identity() -> None:
assert codex_server.mcp.name == "dev-hub-codex"

View file

@ -0,0 +1,92 @@
import os
import subprocess
import tomllib
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SCRIPT = ROOT / "scripts" / "configure-codex.sh"
def run_configure(codex_home: Path, *args: str) -> subprocess.CompletedProcess[str]:
env = os.environ.copy()
env["CODEX_HOME"] = str(codex_home)
return subprocess.run(
[str(SCRIPT), *args],
cwd=ROOT,
env=env,
text=True,
capture_output=True,
check=True,
)
def test_configure_codex_creates_network_setting(tmp_path: Path) -> None:
codex_home = tmp_path / ".codex"
run_configure(codex_home, "--skip-verify", "--skip-mcp")
config = tomllib.loads((codex_home / "config.toml").read_text())
assert config["sandbox_workspace_write"]["network_access"] is True
def test_configure_codex_preserves_existing_settings_and_is_idempotent(
tmp_path: Path,
) -> None:
codex_home = tmp_path / ".codex"
codex_home.mkdir()
config_path = codex_home / "config.toml"
config_path.write_text(
'model = "gpt-test"\n\n'
"[sandbox_workspace_write]\n"
"network_access = false\n"
'writable_roots = ["/tmp/example"]\n'
)
run_configure(codex_home, "--skip-verify", "--skip-mcp")
first = config_path.read_text()
run_configure(codex_home, "--skip-verify", "--skip-mcp")
assert config_path.read_text() == first
config = tomllib.loads(first)
assert config["model"] == "gpt-test"
assert config["sandbox_workspace_write"] == {
"network_access": True,
"writable_roots": ["/tmp/example"],
}
def test_configure_codex_dry_run_does_not_write(tmp_path: Path) -> None:
codex_home = tmp_path / ".codex"
result = run_configure(codex_home, "--dry-run", "--skip-mcp")
assert "DRY-RUN" in result.stdout
assert not (codex_home / "config.toml").exists()
def test_configure_codex_removes_dev_hub_registration_by_default(
tmp_path: Path,
) -> None:
codex_home = tmp_path / ".codex"
codex_home.mkdir()
config_path = codex_home / "config.toml"
config_path.write_text(
'[mcp_servers.dev-hub]\ncommand = "/tmp/dev-hub"\n'
)
result = run_configure(codex_home, "--skip-verify")
assert "removed Codex MCP server dev-hub" in result.stdout
config = tomllib.loads(config_path.read_text())
assert "dev-hub" not in config.get("mcp_servers", {})
def test_configure_codex_mcp_is_explicit_opt_in(tmp_path: Path) -> None:
codex_home = tmp_path / ".codex"
run_configure(codex_home, "--skip-verify", "--with-mcp")
config = tomllib.loads((codex_home / "config.toml").read_text())
command = config["mcp_servers"]["dev-hub"]["command"]
assert command.endswith("/scripts/codex-state-hub-mcp.sh")

View file

@ -48,17 +48,30 @@ def _fake_get(path: str, params: dict | None = None):
if path == "/repos":
return [{"id": "repo-1", "slug": "demo-repo", "domain_slug": "infotech"}]
if path == "/repo-goals":
return [{"id": "goal-1", "title": "Ship it", "description": "d", "priority": "high"}]
return [{
"id": "goal-1",
"repo_id": "repo-1",
"repo_slug": "demo-repo",
"title": "Ship it",
"description": "d",
"priority": "high",
}]
if path == "/capability-catalog/":
return []
return []
def test_get_domain_summary_goal_guidance_is_workplan_first(monkeypatch) -> None:
monkeypatch.setattr(server, "_get", lambda path, params=None: _fake_get(path, params))
calls = []
monkeypatch.setattr(
server,
"_get",
lambda path, params=None: calls.append((path, params)) or _fake_get(path, params),
)
payload = json.loads(server.get_domain_summary("infotech"))
assert "workplans" in payload
assert payload["workstreams"] == payload["workplans"]
action = payload["goal_guidance"]["needs_workplan"][0]["action"]
assert "workplan" in action.lower()
assert "workstream is linked" not in action.lower()
assert "workstream is linked" not in action.lower()
assert [path for path, _ in calls].count("/repo-goals") == 1

View file

@ -0,0 +1,119 @@
from datetime import datetime, timedelta, timezone
import pytest
from api.config import settings
from api.services import ops_run_projection as projection
@pytest.fixture(autouse=True)
def reset_projection_cache() -> None:
projection.reset_ops_run_projection_cache()
@pytest.mark.asyncio
async def test_ops_run_projection_counts_and_contract(monkeypatch) -> None:
now = datetime.now(timezone.utc)
payload = {
"counts": {"open": 2, "claimed": 1, "succeeded": 9, "failed": 2},
"items": [
{
"id": "run-open",
"activity_definition_id": "definition-1",
"target_repo": "state-hub",
"state": "open",
"created_at": (now - timedelta(hours=2)).isoformat(),
"updated_at": (now - timedelta(hours=2)).isoformat(),
"result": {},
},
{
"id": "run-claimed",
"activity_definition_id": "definition-2",
"target_repo": "activity-core",
"state": "claimed",
"claim_owner": "rein-aharness@railiance01",
"lease_until": (now + timedelta(minutes=10)).isoformat(),
"attempt": 1,
"created_at": (now - timedelta(minutes=10)).isoformat(),
"updated_at": now.isoformat(),
"result": {},
},
{
"id": "run-failed",
"activity_definition_id": "definition-3",
"target_repo": "binky-control",
"state": "failed",
"created_at": (now - timedelta(hours=3)).isoformat(),
"updated_at": (now - timedelta(hours=1)).isoformat(),
"result": {"error": "executor timeout"},
},
],
}
async def fake_fetch():
return payload
monkeypatch.setattr(projection, "_fetch_ops_runs", fake_fetch)
result = await projection.get_ops_run_projection(refresh=True)
assert result.available is True
assert result.open == 2
assert result.claimed == 1
assert result.failed_24h == 1
assert result.stuck_open_or_claimed == 1
assert {item.id for item in result.items} == {"run-open", "run-claimed", "run-failed"}
failed = next(item for item in result.items if item.id == "run-failed")
assert failed.last_error == "executor timeout"
claimed = next(item for item in result.items if item.id == "run-claimed")
assert claimed.lease["owner"] == "rein-aharness@railiance01"
@pytest.mark.asyncio
async def test_ops_run_projection_serves_stale_cache_on_refresh_failure(monkeypatch) -> None:
async def initial_fetch():
return {"counts": {"open": 1}, "items": []}
monkeypatch.setattr(projection, "_fetch_ops_runs", initial_fetch)
initial = await projection.get_ops_run_projection(refresh=True)
assert initial.available is True
async def failed_fetch():
raise RuntimeError("upstream down")
monkeypatch.setattr(projection, "_fetch_ops_runs", failed_fetch)
stale = await projection.get_ops_run_projection(refresh=True)
assert stale.available is True
assert stale.stale is True
assert "upstream down" in stale.error
@pytest.mark.asyncio
async def test_ops_run_projection_reports_unconfigured(monkeypatch) -> None:
monkeypatch.setattr(settings, "activity_core_url", None)
result = await projection.get_ops_run_projection(refresh=True)
assert result.available is False
assert "ACTIVITY_CORE_URL is not configured" in result.error
@pytest.mark.asyncio
async def test_ops_runs_summary_route(client, monkeypatch) -> None:
async def fake_fetch():
return {"counts": {"open": 3, "claimed": 2}, "items": []}
monkeypatch.setattr(projection, "_fetch_ops_runs", fake_fetch)
response = await client.get("/ops-runs/summary", params={"refresh": "true"})
assert response.status_code == 200
assert response.json()["open"] == 3
assert response.json()["claimed"] == 2
assert (await client.post("/ops-runs/claim", json={})).status_code == 404
@pytest.mark.asyncio
async def test_state_summary_contains_ops_run_projection(client, monkeypatch) -> None:
async def fake_fetch():
return {"counts": {"open": 4}, "items": []}
monkeypatch.setattr(projection, "_fetch_ops_runs", fake_fetch)
response = await client.get("/state/summary", params={"refresh": "true"})
assert response.status_code == 200
assert response.json()["ops_runs"]["open"] == 4