From 6e3e7e617654e26cd88d96246dfe12bc61ce4c22 Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 6 Jul 2026 10:52:52 +0200 Subject: [PATCH] Include ranked suggestions in daily triage digest (STATE-WP-0061). Expose State Hub ranked_suggestions from /state/summary in the daily_triage_digest resolver so WSJF triage can prioritize gated needs. --- .../context_resolvers/state_hub.py | 21 +++++++++++++++++++ tests/test_state_hub_context_resolver.py | 13 ++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/activity_core/context_resolvers/state_hub.py b/src/activity_core/context_resolvers/state_hub.py index 4de5439..3b7bd78 100644 --- a/src/activity_core/context_resolvers/state_hub.py +++ b/src/activity_core/context_resolvers/state_hub.py @@ -513,11 +513,19 @@ def _daily_triage_digest(params: dict[str, Any]) -> str: max_workstreams = int(params.get("max_workstreams", 12)) max_next_steps = int(params.get("max_next_steps", 8)) open_workstreams = _open_workstream_digest(summary, workplan_index, max_workstreams) + ranked_suggestions = summary.get("ranked_suggestions") or [] + if not isinstance(ranked_suggestions, list): + ranked_suggestions = [] + digest = { "generated_at": summary.get("generated_at"), "totals": summary.get("totals", {}), "open_workstreams": open_workstreams, "next_steps": [_safe_next_step(item) for item in next_steps[:max_next_steps]], + "ranked_suggestions": [ + _safe_ranked_suggestion(item) + for item in ranked_suggestions[: int(params.get("max_suggestions", 10))] + ], "inbox": { "unread_count": len(inbox), "samples": [_safe_inbox_item(item) for item in inbox[:3]], @@ -619,6 +627,19 @@ def _safe_next_step(item: dict[str, Any]) -> dict[str, Any]: } +def _safe_ranked_suggestion(item: dict[str, Any]) -> dict[str, Any]: + return { + "id": item.get("id"), + "title": _short_text(item.get("title", ""), 120), + "stage": item.get("stage"), + "domain_slug": item.get("domain_slug"), + "origin_ref": item.get("origin_ref"), + "relevance": item.get("relevance"), + "wsjf": item.get("wsjf"), + "last_requested_at": item.get("last_requested_at"), + } + + def _safe_inbox_item(item: dict[str, Any]) -> dict[str, Any]: return { "id": item.get("id"), diff --git a/tests/test_state_hub_context_resolver.py b/tests/test_state_hub_context_resolver.py index 0f8727e..0ff5367 100644 --- a/tests/test_state_hub_context_resolver.py +++ b/tests/test_state_hub_context_resolver.py @@ -486,6 +486,18 @@ def test_daily_triage_digest_is_curated_scalar_json(monkeypatch) -> None: "/state/summary": { "generated_at": "2026-05-19T05:20:00Z", "totals": {"tasks": {"todo": 4, "wait": 1}}, + "ranked_suggestions": [ + { + "id": "sug-1", + "title": "Issue-core ingestion API key path", + "stage": "suggestion", + "domain_slug": "custodian", + "origin_ref": "issue-core-ingestion-api-key", + "relevance": 2, + "wsjf": 4.7, + "last_requested_at": "2026-05-19T04:00:00Z", + } + ], "topics": [ { "slug": "custodian", @@ -594,3 +606,4 @@ def test_daily_triage_digest_is_curated_scalar_json(monkeypatch) -> None: assert digest["deterministic_scoring"]["future_mode"] == ( "code_score_high_gain_high_effort_candidates" ) + assert digest["ranked_suggestions"][0]["origin_ref"] == "issue-core-ingestion-api-key"