CUST-WP-0055 T05: workplan-first progress scope in sinks and resolvers
Prefer workplan_id in State Hub progress writes while dual-writing workstream_id for wire-compat. Extend schedule_health, evidence/report sinks, phase5 checks, k8s triage prose, and SCOPE terminology.
This commit is contained in:
parent
7947961ed4
commit
69fe69041f
12 changed files with 135 additions and 30 deletions
|
|
@ -123,6 +123,7 @@ def test_state_hub_progress_sink_posts_compact_probe_summary(monkeypatch) -> Non
|
|||
]
|
||||
body = posts[0]["json"]
|
||||
assert body["summary"] == "Ops inventory probe: 1 ok, 0 degraded, 0 down, 1 skipped"
|
||||
assert body["workplan_id"] == "workstream-1"
|
||||
assert body["workstream_id"] == "workstream-1"
|
||||
assert body["task_id"] == "task-1"
|
||||
assert body["detail"]["activity_core_run_id"] == _run_id()
|
||||
|
|
@ -136,6 +137,35 @@ def test_state_hub_progress_sink_posts_compact_probe_summary(monkeypatch) -> Non
|
|||
assert "token=secret" not in serialized
|
||||
|
||||
|
||||
def test_state_hub_progress_prefers_workplan_id(monkeypatch) -> 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-2"})
|
||||
|
||||
monkeypatch.setattr(httpx, "get", fake_get)
|
||||
monkeypatch.setattr(httpx, "post", fake_post)
|
||||
|
||||
persist_ops_inventory_evidence(
|
||||
_payload([
|
||||
{
|
||||
"type": "state-hub-progress",
|
||||
"state_hub_url": "http://state-hub.test",
|
||||
"event_type": "ops_inventory_probe",
|
||||
"workplan_id": "workplan-1",
|
||||
}
|
||||
])
|
||||
)
|
||||
|
||||
body = posts[0]["json"]
|
||||
assert body["workplan_id"] == "workplan-1"
|
||||
assert body["workstream_id"] == "workplan-1"
|
||||
|
||||
|
||||
def test_core_hub_stabilization_sink_posts_progress(monkeypatch) -> None:
|
||||
posts: list[dict[str, Any]] = []
|
||||
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ def test_state_hub_progress_sink_posts(monkeypatch) -> None:
|
|||
}
|
||||
]
|
||||
assert posts[0]["url"] == "http://state-hub.test/progress/"
|
||||
assert posts[0]["json"]["workplan_id"] == "workstream-1"
|
||||
assert posts[0]["json"]["workstream_id"] == "workstream-1"
|
||||
assert posts[0]["json"]["detail"]["activity_core_run_id"] == payload_run_id()
|
||||
assert posts[0]["json"]["detail"]["output_validated"] is True
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ from __future__ import annotations
|
|||
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from activity_core.schedule_health import evaluate_schedule_health
|
||||
import httpx
|
||||
|
||||
from activity_core.schedule_health import evaluate_schedule_health, post_missed_fire_alert
|
||||
|
||||
NOW = datetime(2026, 6, 23, 12, 0, tzinfo=timezone.utc)
|
||||
|
||||
|
|
@ -70,6 +72,35 @@ def test_no_fire_recorded_for_due_schedule_is_unhealthy() -> None:
|
|||
assert "no recorded fire" in health.reasons[0]
|
||||
|
||||
|
||||
def test_post_missed_fire_alert_dual_writes_workplan_scope(monkeypatch) -> None:
|
||||
posts: list[dict] = []
|
||||
|
||||
class _Resp:
|
||||
def raise_for_status(self) -> None: ...
|
||||
def json(self) -> dict[str, str]:
|
||||
return {"id": "progress-1"}
|
||||
|
||||
monkeypatch.setattr(httpx, "post", lambda url, **kwargs: posts.append(kwargs) or _Resp())
|
||||
|
||||
health = evaluate_schedule_health(
|
||||
activity_id="daily",
|
||||
missed_catchup_window=1,
|
||||
last_fired_at=NOW - timedelta(days=2),
|
||||
now=NOW,
|
||||
expected_interval=timedelta(days=1),
|
||||
)
|
||||
result = post_missed_fire_alert(
|
||||
health,
|
||||
state_hub_url="http://state-hub.test",
|
||||
workplan_id="wp-123",
|
||||
)
|
||||
|
||||
assert result["status"] == "posted"
|
||||
body = posts[0]["json"]
|
||||
assert body["workplan_id"] == "wp-123"
|
||||
assert body["workstream_id"] == "wp-123"
|
||||
|
||||
|
||||
def test_no_interval_and_no_fire_is_not_flagged() -> None:
|
||||
# Without an expected interval we cannot assert a miss from absence alone.
|
||||
health = evaluate_schedule_health(
|
||||
|
|
|
|||
|
|
@ -751,3 +751,5 @@ def test_phase5_stabilization_check_passes(monkeypatch) -> None:
|
|||
assert result["overall_pass"] is True
|
||||
assert result["skipped"] is False
|
||||
assert result["checks"]["totals"]["pass"] is True
|
||||
assert result["checks"]["totals"]["workplans"] == 640
|
||||
assert result["checks"]["totals"]["workstreams"] == 640
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import pytest
|
|||
from activity_core import report_sinks
|
||||
from activity_core.state_hub_write import (
|
||||
IDEMPOTENCY_HEADER,
|
||||
apply_progress_scope_fields,
|
||||
idempotency_headers,
|
||||
idempotency_key,
|
||||
)
|
||||
|
|
@ -33,6 +34,24 @@ def test_headers_carry_the_key() -> None:
|
|||
assert headers == {IDEMPOTENCY_HEADER: "run1:i:e"}
|
||||
|
||||
|
||||
def test_apply_progress_scope_prefers_workplan_id() -> None:
|
||||
body: dict[str, str] = {}
|
||||
apply_progress_scope_fields(
|
||||
body,
|
||||
{"workplan_id": "wp-1", "workstream_id": "ws-legacy", "topic_id": "topic-1"},
|
||||
)
|
||||
assert body["workplan_id"] == "wp-1"
|
||||
assert body["workstream_id"] == "wp-1"
|
||||
assert body["topic_id"] == "topic-1"
|
||||
|
||||
|
||||
def test_apply_progress_scope_dual_writes_legacy_workstream_id() -> None:
|
||||
body: dict[str, str] = {}
|
||||
apply_progress_scope_fields(body, {"workstream_id": "ws-1"})
|
||||
assert body["workplan_id"] == "ws-1"
|
||||
assert body["workstream_id"] == "ws-1"
|
||||
|
||||
|
||||
def test_distinct_identities_get_distinct_keys() -> None:
|
||||
assert idempotency_key("r", "i", "daily_triage") != idempotency_key(
|
||||
"r", "i", "schedule_miss"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue