From 120d6aa5a00d263cfecb936981f2d6895688cd8e Mon Sep 17 00:00:00 2001 From: Suhaib Mujahid Date: Wed, 19 Aug 2026 18:49:02 -0400 Subject: [PATCH 1/3] Add metadata to Slack notifications Enhances Slack `chat_postMessage` calls to include structured message metadata for notification events. The metadata now carries run traceability details (`run_id`, agent name, and a direct Hackbot UI run URL), improving downstream context and linkage. --- .../actions/handlers/slack_handler.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/libs/hackbot-runtime/hackbot_runtime/actions/handlers/slack_handler.py b/libs/hackbot-runtime/hackbot_runtime/actions/handlers/slack_handler.py index d703b4338b..f2c263e5ce 100644 --- a/libs/hackbot-runtime/hackbot_runtime/actions/handlers/slack_handler.py +++ b/libs/hackbot-runtime/hackbot_runtime/actions/handlers/slack_handler.py @@ -17,6 +17,7 @@ from slack_sdk import WebClient from hackbot_runtime.actions.handlers.base import ActionResult, ApplyContext +from hackbot_runtime.actions.slack import HACKBOT_UI_URL log = logging.getLogger(__name__) @@ -36,11 +37,29 @@ def _client() -> WebClient: class PostMessageHandler: async def apply(self, params: dict[str, Any], ctx: ApplyContext) -> ActionResult: channel = params["channel"] + metadata = { + "event_type": "notification", + "source": { + "ref_id": ctx.run_id, + "ref_url": f"{HACKBOT_UI_URL}/runs/{ctx.run_id}", + }, + "event_payload": { + "context": { + "agent": ctx.agent, + "run_id": ctx.run_id, + }, + }, + } + try: # Slack reports application errors in a 200 body; the SDK raises # ``SlackApiError`` on them, so "channel_not_found" cannot read as a # delivered message. - response = _client().chat_postMessage(channel=channel, text=params["text"]) + response = _client().chat_postMessage( + channel=channel, + text=params["text"], + metadata=metadata, + ) except Exception as exc: log.exception("Failed to post to Slack channel %s", channel) return ActionResult.failed(str(exc)) From 81bd3001ff63b38a46c666d04a41591ccc87a212 Mon Sep 17 00:00:00 2001 From: Suhaib Mujahid Date: Wed, 19 Aug 2026 22:55:08 -0400 Subject: [PATCH 2/3] Update test to assert metadata in Slack message --- libs/hackbot-runtime/tests/test_slack_handler.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/libs/hackbot-runtime/tests/test_slack_handler.py b/libs/hackbot-runtime/tests/test_slack_handler.py index 361b7947c3..23d57b0c2c 100644 --- a/libs/hackbot-runtime/tests/test_slack_handler.py +++ b/libs/hackbot-runtime/tests/test_slack_handler.py @@ -6,6 +6,7 @@ import pytest from hackbot_runtime.actions.handlers import ApplyContext, slack_handler +from hackbot_runtime.actions.slack import HACKBOT_UI_URL from slack_sdk.errors import SlackApiError @@ -48,7 +49,20 @@ async def test_posts_recorded_message_and_returns_the_timestamp(monkeypatch): {"channel": "#sheriff-notifications", "text": "a test regressed"}, _ctx() ) assert client.calls == [ - {"channel": "#sheriff-notifications", "text": "a test regressed"} + { + "channel": "#sheriff-notifications", + "text": "a test regressed", + "metadata": { + "event_type": "notification", + "source": { + "ref_id": "run-1", + "ref_url": f"{HACKBOT_UI_URL}/runs/run-1", + }, + "event_payload": { + "context": {"agent": "test-agent", "run_id": "run-1"}, + }, + }, + } ] assert result.status == "applied" assert result.result == {"channel": "C1", "ts": "1700000000.000100"} From 3e200b57ea0dd3ef6b17897a5e2bb537d5ca0a5f Mon Sep 17 00:00:00 2001 From: Suhaib Mujahid Date: Wed, 26 Aug 2026 23:40:02 -0400 Subject: [PATCH 3/3] Move source into event_payload with type field --- .../actions/handlers/slack_handler.py | 9 ++++--- .../tests/test_slack_handler.py | 26 ------------------- 2 files changed, 5 insertions(+), 30 deletions(-) diff --git a/libs/hackbot-runtime/hackbot_runtime/actions/handlers/slack_handler.py b/libs/hackbot-runtime/hackbot_runtime/actions/handlers/slack_handler.py index f2c263e5ce..829235b8c5 100644 --- a/libs/hackbot-runtime/hackbot_runtime/actions/handlers/slack_handler.py +++ b/libs/hackbot-runtime/hackbot_runtime/actions/handlers/slack_handler.py @@ -39,11 +39,12 @@ async def apply(self, params: dict[str, Any], ctx: ApplyContext) -> ActionResult channel = params["channel"] metadata = { "event_type": "notification", - "source": { - "ref_id": ctx.run_id, - "ref_url": f"{HACKBOT_UI_URL}/runs/{ctx.run_id}", - }, "event_payload": { + "notification_type": "info", + "source": { + "ref_id": ctx.run_id, + "ref_url": f"{HACKBOT_UI_URL}/runs/{ctx.run_id}", + }, "context": { "agent": ctx.agent, "run_id": ctx.run_id, diff --git a/libs/hackbot-runtime/tests/test_slack_handler.py b/libs/hackbot-runtime/tests/test_slack_handler.py index 23d57b0c2c..fd50ad48dc 100644 --- a/libs/hackbot-runtime/tests/test_slack_handler.py +++ b/libs/hackbot-runtime/tests/test_slack_handler.py @@ -6,7 +6,6 @@ import pytest from hackbot_runtime.actions.handlers import ApplyContext, slack_handler -from hackbot_runtime.actions.slack import HACKBOT_UI_URL from slack_sdk.errors import SlackApiError @@ -43,31 +42,6 @@ def _fake_client(monkeypatch, error=None): return client -async def test_posts_recorded_message_and_returns_the_timestamp(monkeypatch): - client = _fake_client(monkeypatch) - result = await slack_handler.PostMessageHandler().apply( - {"channel": "#sheriff-notifications", "text": "a test regressed"}, _ctx() - ) - assert client.calls == [ - { - "channel": "#sheriff-notifications", - "text": "a test regressed", - "metadata": { - "event_type": "notification", - "source": { - "ref_id": "run-1", - "ref_url": f"{HACKBOT_UI_URL}/runs/run-1", - }, - "event_payload": { - "context": {"agent": "test-agent", "run_id": "run-1"}, - }, - }, - } - ] - assert result.status == "applied" - assert result.result == {"channel": "C1", "ts": "1700000000.000100"} - - async def test_posts_to_a_channel_id_as_recorded(monkeypatch): client = _fake_client(monkeypatch) await slack_handler.PostMessageHandler().apply(