Set up daily WSJF closure gates

This commit is contained in:
2026-06-07 11:00:03 +02:00
parent 418eb4ffda
commit 4e8ccbb344
9 changed files with 431 additions and 4 deletions

View File

@@ -52,6 +52,18 @@ class _BadLLM:
return "not valid json {"
class _FailingLLM:
"""Raises like a missing or unreachable llm-connect endpoint."""
def complete(
self,
prompt: str,
model: str = "",
config: dict | None = None,
) -> str:
raise RuntimeError("LLM_CONNECT_URL is not configured")
class _CountingLLM:
"""Tracks how many times complete() is called; returns bad JSON then good JSON."""
@@ -429,6 +441,30 @@ def test_execute_instruction_with_audit_preserves_invalid_report_with_sinks(
assert llm.call_count == 2
def test_execute_instruction_with_audit_preserves_execution_failure_with_sinks():
instr = _instr(
id="daily-triage-report",
prompt="Report.",
trusted_fields=[],
report_sinks=[{"type": "working-memory", "path": "/tmp"}],
)
result = execute_instruction_with_audit(instr, _Event(), {}, _FailingLLM())
assert result.tasks == []
assert result.output_validated is False
assert result.review_required is True
assert result.validation_error == "LLM_CONNECT_URL is not configured"
assert result.report == {
"summary": (
"Instruction daily-triage-report could not run; "
"operator review is required."
),
"status": "execution_failed",
"validation_error": "LLM_CONNECT_URL is not configured",
}
def test_execute_instruction_with_audit_accepts_report_and_tasks_envelope():
envelope = {
"report": {"summary": "Review needed."},

View File

@@ -33,6 +33,8 @@ def _by_kind_name(kind: str, name: str) -> dict[str, Any]:
def test_runtime_config_has_ops_inventory_placeholders() -> None:
config = _by_kind_name("ConfigMap", "actcore-runtime-config")
assert config["data"]["LLM_CONNECT_URL"] == ""
assert config["data"]["LLM_CONNECT_TIMEOUT_SECONDS"] == "300"
assert config["data"]["OPS_INVENTORY_PATH"] == (
"/etc/activity-core/ops/service-inventory.yml"
)
@@ -74,6 +76,28 @@ def test_external_configmap_projects_disabled_ops_probe_definition(tmp_path) ->
]
def test_external_configmap_projects_enabled_daily_wsjf_definition(tmp_path) -> None:
config = _by_kind_name("ConfigMap", "actcore-external-activity-definitions")
raw_definition = config["data"]["daily-statehub-wsjf-triage.md"]
definition_path = tmp_path / "daily-statehub-wsjf-triage.md"
definition_path.write_text(raw_definition, encoding="utf-8")
definition = parse_file(definition_path)
instruction = definition.instructions[0]
assert definition.id == "6fca51fa-387a-4fd0-bc4e-d62c29eb859a"
assert definition.name == "Daily State Hub WSJF Triage"
assert definition.enabled is True
assert definition.trigger_config["cron_expression"] == "20 7 * * *"
assert definition.trigger_config["timezone"] == "Europe/Berlin"
assert instruction["id"] == "daily-triage-report"
assert instruction["output_schema"] == (
"/etc/activity-core/schemas/daily-triage-report.json"
)
assert instruction["report_sinks"][0]["type"] == "working-memory"
assert instruction["report_sinks"][1]["event_type"] == "daily_triage"
def test_ops_inventory_configmap_contains_probeable_inventory() -> None:
config = _by_kind_name("ConfigMap", "actcore-ops-service-inventory")
inventory = yaml.safe_load(config["data"]["service-inventory.yml"])
@@ -104,6 +128,27 @@ def test_worker_mounts_ops_inventory_configmap() -> None:
)
def test_worker_mounts_daily_triage_schema_and_working_memory() -> None:
deployment = _by_kind_name("Deployment", "actcore-worker")
pod_spec = deployment["spec"]["template"]["spec"]
container = pod_spec["containers"][0]
mounts = {mount["name"]: mount for mount in container["volumeMounts"]}
volumes = {volume["name"]: volume for volume in pod_spec["volumes"]}
schema_config = _by_kind_name("ConfigMap", "actcore-report-schemas")
assert "daily-triage-report.json" in schema_config["data"]
assert mounts["report-schemas"]["mountPath"] == "/etc/activity-core/schemas"
assert mounts["report-schemas"]["readOnly"] is True
assert volumes["report-schemas"]["configMap"]["name"] == "actcore-report-schemas"
assert mounts["working-memory"]["mountPath"] == (
"/home/worsch/the-custodian/memory/working"
)
assert volumes["working-memory"]["persistentVolumeClaim"]["claimName"] == (
"actcore-working-memory"
)
def test_ops_hub_key_is_secret_only_placeholder() -> None:
runtime_config = _by_kind_name("ConfigMap", "actcore-runtime-config")
bootstrap = _BOOTSTRAP_SECRETS_PATH.read_text(encoding="utf-8")

View File

@@ -115,6 +115,42 @@ def test_state_hub_progress_sink_posts(monkeypatch) -> None:
assert posts[0]["json"]["detail"]["review_required"] is False
def test_state_hub_progress_includes_prior_working_memory_path(
monkeypatch,
tmp_path,
) -> None:
posts: list[dict[str, Any]] = []
def fake_get(url: str, **kwargs: Any) -> DummyResponse:
return DummyResponse([])
def fake_post(url: str, **kwargs: Any) -> DummyResponse:
posts.append({"url": url, **kwargs})
return DummyResponse({"id": "progress-1"})
monkeypatch.setattr(httpx, "get", fake_get)
monkeypatch.setattr(httpx, "post", fake_post)
result = persist_reports(_payload([
{
"type": "working-memory",
"path": str(tmp_path),
"timezone": "Europe/Berlin",
},
{
"type": "state-hub-progress",
"state_hub_url": "http://state-hub.test",
"event_type": "daily_triage",
},
]))
assert [entry["status"] for entry in result] == ["written", "posted"]
assert posts[0]["json"]["detail"]["working_memory_path"] == str(
tmp_path / "daily-triage-2026-05-19-12345678.md"
)
assert posts[0]["json"]["detail"]["working_memory_status"] == "written"
def test_state_hub_progress_sink_is_idempotent(monkeypatch) -> None:
def fake_get(url: str, **kwargs: Any) -> DummyResponse:
return DummyResponse([