From 44a2d0d4857da010b3136bed00c5a88bfe8a90fb Mon Sep 17 00:00:00 2001 From: Tamir Dresher Date: Thu, 26 Mar 2026 01:05:15 +0200 Subject: [PATCH 1/6] feat(skills): add notification-routing skill -- multi-channel pub-sub routing for agent notifications --- .squad/skills/notification-routing/SKILL.md | 141 ++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 .squad/skills/notification-routing/SKILL.md diff --git a/.squad/skills/notification-routing/SKILL.md b/.squad/skills/notification-routing/SKILL.md new file mode 100644 index 000000000..dc94162b3 --- /dev/null +++ b/.squad/skills/notification-routing/SKILL.md @@ -0,0 +1,141 @@ +--- +name: notification-routing +description: Route agent notifications to specific channels by type instead of flooding a single channel +domain: communication, observability, multi-agent +confidence: high +source: earned (notification firehose incident — 2026-03, tamirdresher/tamresearch1) +--- + +## Context + +When a Squad grows beyond a few agents, notifications flood a single channel — failure alerts drown in daily +briefings, tech news buries security findings, and everything gets ignored. This is the microservices equivalent +of dumping every service's logs into one file. + +The fix is **pub-sub with topic routing**: agents tag notifications with a channel type, and a routing function +sends them to the appropriate destination. + +**Trigger symptoms:** +- Important alerts missed because they're buried in routine notifications +- Team members turning off notifications entirely (signal overwhelm) +- Onboarding friction: "where do I look for X?" + +## Patterns + +### Channel Config File + +Define a `teams-channels.json` config (or equivalent) mapping notification types to channel names: + +```json +{ + "channels": { + "notifications": "squad-alerts", + "tech-news": "tech-news", + "security": "security-findings", + "releases": "release-announcements", + "daily-digest": "daily-digest" + } +} +``` + +Place this in `.squad/teams-channels.json` (git-tracked, shared across the team). + +### Routing in PowerShell + +```powershell +function Send-TeamNotification { + param( + [string]$Message, + [string]$Channel = "notifications", # default channel + [string]$Urgency = "normal" # normal | high | low + ) + + $config = Get-Content ".squad\teams-channels.json" | ConvertFrom-Json + $targetChannel = $config.channels.$Channel ?? $config.channels.notifications + + # Format by urgency + $prefix = switch ($Urgency) { + "high" { "🚨" } + "low" { "â„šī¸" } + default { "đŸ“ĸ" } + } + + Send-TeamsMessage -Channel $targetChannel -Text "$prefix $Message" +} + +# Usage: +Send-TeamNotification -Message "37 consecutive failures" -Channel "notifications" -Urgency "high" +Send-TeamNotification -Message "Today's tech digest: ..." -Channel "tech-news" -Urgency "low" +``` + +### Channel Tag Convention + +Agents can tag their output with `CHANNEL:` metadata for the notification dispatcher to read: + +``` +CHANNEL:security +Worf found 3 new CVEs in dependency scan: lodash@4.17.15, ... +``` + +The dispatcher reads the tag and routes accordingly: + +```powershell +function Dispatch-Notification { + param([string]$RawOutput) + + if ($RawOutput -match '^CHANNEL:(\w[\w-]*)') { + $channel = $matches[1] + $message = $RawOutput -replace '^CHANNEL:\w[\w-]*\r?\n', '' + Send-TeamNotification -Message $message -Channel $channel + } else { + Send-TeamNotification -Message $RawOutput # default channel + } +} +``` + +### Service Discovery: Avoid Name Collisions + +When creating Teams channels, verify you're in the **correct team** before sending. Teams channel names look +similar across teams (e.g., "Squad" vs "squads" vs "my-squad"). Always resolve the channel ID, not just the +name: + +```powershell +# Bad: relies on name uniqueness +Send-TeamsMessage -TeamName "Squad" -ChannelName "notifications" -Text $msg # ❌ Wrong "Squad"? + +# Good: resolve ID once, cache it +$channelId = Get-TeamsChannelId -TeamId $config.teamId -ChannelName $config.channels.notifications +Send-TeamsMessageById -ChannelId $channelId -Text $msg # ✅ Unambiguous +``` + +Store resolved channel IDs in `.squad/teams-channels.json` alongside names: + +```json +{ + "teamId": "abc-123-def", + "channels": { + "notifications": { "name": "squad-alerts", "id": "19:channel-id@thread.tacv2" }, + "tech-news": { "name": "tech-news", "id": "19:channel-id-2@thread.tacv2" } + } +} +``` + +## Anti-Patterns + +**NEVER send all notification types to one channel:** +```powershell +# ❌ All noise, no signal — eventually everyone ignores everything +Send-TeamsMessage -Channel "general" -Text $anything +``` + +**NEVER use team/channel display names as identifiers:** +```powershell +# ❌ "Squad" and "squads" are different teams — name collisions happen +$team = Find-TeamsTeam -Name "Squad" # Which "Squad"? +``` + +## Distributed Systems Pattern + +This is **pub-sub with topic routing** — the same principle as Kafka topics, RabbitMQ routing keys, and AWS +SNS topic filtering. The lesson: a single message queue for everything is a recipe for missed alerts. Route by +type. Each consumer subscribes to the topics it cares about. From 3ab8f1be59d8821043ccf3c9f888096d252a0cba Mon Sep 17 00:00:00 2001 From: Tamir Dresher Date: Thu, 26 Mar 2026 01:06:23 +0200 Subject: [PATCH 2/6] chore: remove incorrectly placed skill file --- .squad/skills/notification-routing/SKILL.md | 141 -------------------- 1 file changed, 141 deletions(-) delete mode 100644 .squad/skills/notification-routing/SKILL.md diff --git a/.squad/skills/notification-routing/SKILL.md b/.squad/skills/notification-routing/SKILL.md deleted file mode 100644 index dc94162b3..000000000 --- a/.squad/skills/notification-routing/SKILL.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -name: notification-routing -description: Route agent notifications to specific channels by type instead of flooding a single channel -domain: communication, observability, multi-agent -confidence: high -source: earned (notification firehose incident — 2026-03, tamirdresher/tamresearch1) ---- - -## Context - -When a Squad grows beyond a few agents, notifications flood a single channel — failure alerts drown in daily -briefings, tech news buries security findings, and everything gets ignored. This is the microservices equivalent -of dumping every service's logs into one file. - -The fix is **pub-sub with topic routing**: agents tag notifications with a channel type, and a routing function -sends them to the appropriate destination. - -**Trigger symptoms:** -- Important alerts missed because they're buried in routine notifications -- Team members turning off notifications entirely (signal overwhelm) -- Onboarding friction: "where do I look for X?" - -## Patterns - -### Channel Config File - -Define a `teams-channels.json` config (or equivalent) mapping notification types to channel names: - -```json -{ - "channels": { - "notifications": "squad-alerts", - "tech-news": "tech-news", - "security": "security-findings", - "releases": "release-announcements", - "daily-digest": "daily-digest" - } -} -``` - -Place this in `.squad/teams-channels.json` (git-tracked, shared across the team). - -### Routing in PowerShell - -```powershell -function Send-TeamNotification { - param( - [string]$Message, - [string]$Channel = "notifications", # default channel - [string]$Urgency = "normal" # normal | high | low - ) - - $config = Get-Content ".squad\teams-channels.json" | ConvertFrom-Json - $targetChannel = $config.channels.$Channel ?? $config.channels.notifications - - # Format by urgency - $prefix = switch ($Urgency) { - "high" { "🚨" } - "low" { "â„šī¸" } - default { "đŸ“ĸ" } - } - - Send-TeamsMessage -Channel $targetChannel -Text "$prefix $Message" -} - -# Usage: -Send-TeamNotification -Message "37 consecutive failures" -Channel "notifications" -Urgency "high" -Send-TeamNotification -Message "Today's tech digest: ..." -Channel "tech-news" -Urgency "low" -``` - -### Channel Tag Convention - -Agents can tag their output with `CHANNEL:` metadata for the notification dispatcher to read: - -``` -CHANNEL:security -Worf found 3 new CVEs in dependency scan: lodash@4.17.15, ... -``` - -The dispatcher reads the tag and routes accordingly: - -```powershell -function Dispatch-Notification { - param([string]$RawOutput) - - if ($RawOutput -match '^CHANNEL:(\w[\w-]*)') { - $channel = $matches[1] - $message = $RawOutput -replace '^CHANNEL:\w[\w-]*\r?\n', '' - Send-TeamNotification -Message $message -Channel $channel - } else { - Send-TeamNotification -Message $RawOutput # default channel - } -} -``` - -### Service Discovery: Avoid Name Collisions - -When creating Teams channels, verify you're in the **correct team** before sending. Teams channel names look -similar across teams (e.g., "Squad" vs "squads" vs "my-squad"). Always resolve the channel ID, not just the -name: - -```powershell -# Bad: relies on name uniqueness -Send-TeamsMessage -TeamName "Squad" -ChannelName "notifications" -Text $msg # ❌ Wrong "Squad"? - -# Good: resolve ID once, cache it -$channelId = Get-TeamsChannelId -TeamId $config.teamId -ChannelName $config.channels.notifications -Send-TeamsMessageById -ChannelId $channelId -Text $msg # ✅ Unambiguous -``` - -Store resolved channel IDs in `.squad/teams-channels.json` alongside names: - -```json -{ - "teamId": "abc-123-def", - "channels": { - "notifications": { "name": "squad-alerts", "id": "19:channel-id@thread.tacv2" }, - "tech-news": { "name": "tech-news", "id": "19:channel-id-2@thread.tacv2" } - } -} -``` - -## Anti-Patterns - -**NEVER send all notification types to one channel:** -```powershell -# ❌ All noise, no signal — eventually everyone ignores everything -Send-TeamsMessage -Channel "general" -Text $anything -``` - -**NEVER use team/channel display names as identifiers:** -```powershell -# ❌ "Squad" and "squads" are different teams — name collisions happen -$team = Find-TeamsTeam -Name "Squad" # Which "Squad"? -``` - -## Distributed Systems Pattern - -This is **pub-sub with topic routing** — the same principle as Kafka topics, RabbitMQ routing keys, and AWS -SNS topic filtering. The lesson: a single message queue for everything is a recipe for missed alerts. Route by -type. Each consumer subscribes to the topics it cares about. From 01377c24089db5fbfbe39b598ee9e9143fff91df Mon Sep 17 00:00:00 2001 From: Tamir Dresher Date: Thu, 26 Mar 2026 01:07:14 +0200 Subject: [PATCH 3/6] feat(skills): add notification-routing skill -- multi-channel pub-sub routing for agent notifications --- .../skills/notification-routing/SKILL.md | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 packages/squad-cli/templates/skills/notification-routing/SKILL.md diff --git a/packages/squad-cli/templates/skills/notification-routing/SKILL.md b/packages/squad-cli/templates/skills/notification-routing/SKILL.md new file mode 100644 index 000000000..2b2df1fec --- /dev/null +++ b/packages/squad-cli/templates/skills/notification-routing/SKILL.md @@ -0,0 +1,105 @@ +--- +name: "notification-routing" +description: "Route agent notifications to specific channels by type — prevent alert fatigue from single-channel flooding" +domain: "communication" +confidence: "high" +source: "earned" +--- + +## Context + +When a Squad grows beyond a few agents, notifications flood a single channel — failure alerts drown in daily +briefings, tech news buries security findings, and everything gets ignored. This is the pub-sub problem: +a single message queue for everything is a recipe for missed alerts. + +The fix is **topic-based routing**: agents tag notifications with a channel type, and a routing function +sends them to the appropriate destination. + +**Trigger symptoms:** +- Important alerts missed because they're buried in routine notifications +- Team members turning off notifications entirely (signal overwhelm) +- Onboarding friction: "where do I look for X?" + +## Patterns + +### Channel Config Schema + +Define a `.squad/teams-channels.json` (or equivalent) mapping notification types to channel identifiers: + +```json +{ + "teamId": "your-team-id", + "channels": { + "notifications": "squad-alerts", + "tech-news": "tech-news", + "security": "security-findings", + "releases": "release-announcements", + "daily-digest": "daily-digest" + } +} +``` + +Place this in `.squad/` (git-tracked, shared across the team). For platforms that use channel IDs instead of +names (Teams, Slack), store the resolved ID alongside the name to avoid name-collision bugs: + +```json +{ + "channels": { + "notifications": { "name": "squad-alerts", "id": "channel-id-opaque-string" } + } +} +``` + +### CHANNEL: Tag Convention + +Agents prefix their output with `CHANNEL:` to signal where the notification should go: + +``` +CHANNEL:security +Worf found 3 new CVEs in dependency scan: lodash@4.17.15, minimist@1.2.5 +``` + +### Routing Dispatcher (shell pseudocode) + +```bash +dispatch_notification() { + local raw_output="$1" + local channel="notifications" # default + + if echo "$raw_output" | grep -qE '^CHANNEL:[a-z][a-z0-9-]*'; then + channel=$(echo "$raw_output" | head -1 | cut -d: -f2) + raw_output=$(echo "$raw_output" | tail -n +2) + fi + + send_notification --channel "$channel" --message "$raw_output" +} +``` + +### Provider-Agnostic Adapter + +The routing layer is provider-agnostic. Plug in your platform adapter: + +``` +.squad/notify-adapter.sh # Teams / Slack / Discord / webhook -- swappable +``` + +The routing config and CHANNEL: tags never change. Only the adapter changes per deployment. + +## Anti-Patterns + +**Never send all notification types to one channel:** +``` +send_notification --channel "general" --message "$anything" +``` + +**Never use display names as identifiers (name collision risk):** +``` +send_to_team --name "Squad" --channel "notifications" +``` + +Resolve channel IDs once at setup. Use IDs at runtime. + +## Distributed Systems Pattern + +This is **pub-sub with topic routing** -- the same principle as Kafka topics, RabbitMQ routing keys, and +AWS SNS topic filtering. Route by type. Each consumer subscribes to the topics it cares about. \ No newline at end of file From 05ea83b26f7eee625441c5850a044ba737c646cd Mon Sep 17 00:00:00 2001 From: Tamir Dresher Date: Thu, 26 Mar 2026 01:07:31 +0200 Subject: [PATCH 4/6] feat(skills): add notification-routing skill to squad-sdk templates --- .../skills/notification-routing/SKILL.md | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 packages/squad-sdk/templates/skills/notification-routing/SKILL.md diff --git a/packages/squad-sdk/templates/skills/notification-routing/SKILL.md b/packages/squad-sdk/templates/skills/notification-routing/SKILL.md new file mode 100644 index 000000000..2b2df1fec --- /dev/null +++ b/packages/squad-sdk/templates/skills/notification-routing/SKILL.md @@ -0,0 +1,105 @@ +--- +name: "notification-routing" +description: "Route agent notifications to specific channels by type — prevent alert fatigue from single-channel flooding" +domain: "communication" +confidence: "high" +source: "earned" +--- + +## Context + +When a Squad grows beyond a few agents, notifications flood a single channel — failure alerts drown in daily +briefings, tech news buries security findings, and everything gets ignored. This is the pub-sub problem: +a single message queue for everything is a recipe for missed alerts. + +The fix is **topic-based routing**: agents tag notifications with a channel type, and a routing function +sends them to the appropriate destination. + +**Trigger symptoms:** +- Important alerts missed because they're buried in routine notifications +- Team members turning off notifications entirely (signal overwhelm) +- Onboarding friction: "where do I look for X?" + +## Patterns + +### Channel Config Schema + +Define a `.squad/teams-channels.json` (or equivalent) mapping notification types to channel identifiers: + +```json +{ + "teamId": "your-team-id", + "channels": { + "notifications": "squad-alerts", + "tech-news": "tech-news", + "security": "security-findings", + "releases": "release-announcements", + "daily-digest": "daily-digest" + } +} +``` + +Place this in `.squad/` (git-tracked, shared across the team). For platforms that use channel IDs instead of +names (Teams, Slack), store the resolved ID alongside the name to avoid name-collision bugs: + +```json +{ + "channels": { + "notifications": { "name": "squad-alerts", "id": "channel-id-opaque-string" } + } +} +``` + +### CHANNEL: Tag Convention + +Agents prefix their output with `CHANNEL:` to signal where the notification should go: + +``` +CHANNEL:security +Worf found 3 new CVEs in dependency scan: lodash@4.17.15, minimist@1.2.5 +``` + +### Routing Dispatcher (shell pseudocode) + +```bash +dispatch_notification() { + local raw_output="$1" + local channel="notifications" # default + + if echo "$raw_output" | grep -qE '^CHANNEL:[a-z][a-z0-9-]*'; then + channel=$(echo "$raw_output" | head -1 | cut -d: -f2) + raw_output=$(echo "$raw_output" | tail -n +2) + fi + + send_notification --channel "$channel" --message "$raw_output" +} +``` + +### Provider-Agnostic Adapter + +The routing layer is provider-agnostic. Plug in your platform adapter: + +``` +.squad/notify-adapter.sh # Teams / Slack / Discord / webhook -- swappable +``` + +The routing config and CHANNEL: tags never change. Only the adapter changes per deployment. + +## Anti-Patterns + +**Never send all notification types to one channel:** +``` +send_notification --channel "general" --message "$anything" +``` + +**Never use display names as identifiers (name collision risk):** +``` +send_to_team --name "Squad" --channel "notifications" +``` + +Resolve channel IDs once at setup. Use IDs at runtime. + +## Distributed Systems Pattern + +This is **pub-sub with topic routing** -- the same principle as Kafka topics, RabbitMQ routing keys, and +AWS SNS topic filtering. Route by type. Each consumer subscribes to the topics it cares about. \ No newline at end of file From 7bda09f623a3c65b6791f8342c156181eb31ec6f Mon Sep 17 00:00:00 2001 From: Tamir Dresher Date: Thu, 26 Mar 2026 01:07:42 +0200 Subject: [PATCH 5/6] chore: add changeset for notification-routing skill --- .changeset/notification-routing-skill.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/notification-routing-skill.md diff --git a/.changeset/notification-routing-skill.md b/.changeset/notification-routing-skill.md new file mode 100644 index 000000000..7f71556f3 --- /dev/null +++ b/.changeset/notification-routing-skill.md @@ -0,0 +1,6 @@ +--- +"@bradygaster/squad-cli": minor +"@bradygaster/squad-sdk": minor +--- + +feat: add notification-routing skill for pub-sub channel routing \ No newline at end of file From bf8550a143691c78edc1a947652bd753157c5993 Mon Sep 17 00:00:00 2001 From: Tamir Dresher Date: Thu, 26 Mar 2026 01:08:18 +0200 Subject: [PATCH 6/6] chore: fix changeset package names for notification-routing skill