Skip to content

feat(controller): add per-flow Slack config for incident triage#46

Merged
sarahjanenewnham merged 2 commits into
mainfrom
feat/incident-triage-slack-overrides
May 13, 2026
Merged

feat(controller): add per-flow Slack config for incident triage#46
sarahjanenewnham merged 2 commits into
mainfrom
feat/incident-triage-slack-overrides

Conversation

@sarahjanenewnham

@sarahjanenewnham sarahjanenewnham commented May 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds two fields under config.incident_triage so the ticketing and incident-triage flows can post to their own Slack channels with their own bot tokens, side by side:

  • slack_channel_id — the Slack channel for the incident-triage agent Job's MCP-based notifications.
  • slack_token_secret — the Kubernetes Secret backing SLACK_BOT_TOKEN for the same Job. Probed for well-known keys (SLACK_BOT_TOKEN, SLACK_TOKEN) with a fallback to the literal token key, matching slackSecretKeyRefs.

When both fields are empty, the incident flow falls back to the first configured channel under notifications.channels — single-channel deployments keep working without changes.

Why

The ticketing flow reads its Slack channel from the first entry under notifications.channels. The incident-triage flow had nowhere to read its own channel from — every agent inherited the ticketing channel, so a deployment that wanted both flows live (ours, for example) had no way to separate their Slack output. Reordering the notifications list would just move the problem to ticketing.

Both flows are first-class. Each reads its Slack channel from its own YAML location:

notifications:
  channels:
    - backend: slack
      config:
        channel_id: "C_TICKETING"     # ticketing flow
        token_secret: ticketing-bot

incident_triage:
  engine: claude-code
  append_system_prompt: |
    ...
  slack_channel_id: "C_INCIDENT"      # incident flow
  slack_token_secret: incident-bot

A single Osmia deployment can run both flows concurrently with separate channels and bot tokens — the two reconcilers (ProcessTicket, ProcessIncidentEvent) build their per-call EngineConfig independently, so each agent Job ends up with the right SLACK_CHANNEL_ID and SLACK_BOT_TOKEN SecretKeyRef for its flow.

What's added

  • internal/config/config.goIncidentTriageConfig gains SlackChannelID and SlackTokenSecret.
  • internal/controller/controller.go — new resolveSlackTokenKey(ctx, tokenSecret) helper that probes a Secret for SLACK_BOT_TOKEN/SLACK_TOKEN and falls back to token, matching slackSecretKeyRefs's default key resolution.
  • internal/controller/incident.goProcessIncidentEvent reads the per-flow Slack settings off IncidentTriageConfig and sets them on the per-call EngineConfig.Env and EngineConfig.SecretKeyRefs, layered on top of baseEngineConfig exactly like the existing AppendSystemPrompt field.
  • Tests — four new tests covering: posting to the configured channel, falling back to the ticketing channel when the field is empty, using the configured token Secret with the well-known-key probe, and falling back to the literal token key when the Secret lacks a well-known name.
  • Docs — two new rows in the Dispatch-configuration table of docs/api/webhooks.md, plus a paired example in the Example configuration section showing both flows side by side.
  • CHANGELOG — entry under [Unreleased] > Added.

Test plan

  • go build ./... clean.
  • go test ./internal/controller/... -run TestProcessIncidentEvent -v — all 11 incident tests pass (7 existing + 4 new).
  • go test ./... — full suite green, no regressions.
  • gofmt -l clean on touched files.
  • End-to-end verification deferred to the deploying cluster: set incident_triage.slack_channel_id to a separate channel from notifications.channels[0] and confirm the next agent run posts there.

Summary by CodeRabbit

  • New Features

    • Optional per-incident-triage Slack channel and bot-token secret overrides so triage notifications can be routed to a dedicated Slack channel with a specific bot token independent of default notifications. Token resolution probes common secret keys before falling back to the default.
  • Documentation

    • Updated webhook and changelog docs with new incident-triage routing fields and examples.
  • Tests

    • Added tests covering channel/token override and fallback behaviors.

Review Change Stack

ProcessIncidentEvent spawns agent Jobs that inherit SLACK_CHANNEL_ID
and SLACK_BOT_TOKEN from the first configured channel under
config.notifications.channels — the same default that applies to
ticketing runs. There is no way today to send an incident-triage
agent's MCP-based Slack output to a different channel without
affecting where ticketing notifications go, which forces operators
to either co-mingle ticketing and incident-triage output in one
channel, or reorder the notifications list and have ticketing posts
go to the wrong place.

Adds two optional fields to the IncidentTriage block:

- slack_channel_id replaces SLACK_CHANNEL_ID on incident-triage
  agent Jobs only.
- slack_token_secret replaces the SLACK_BOT_TOKEN SecretKeyRef on
  the same Jobs, with the same well-known-key probe used by
  slackSecretKeyRefs (SLACK_BOT_TOKEN, SLACK_TOKEN, then the literal
  "token" key as a fallback).

The probe logic is extracted as resolveSlackTokenKey on the
Reconciler so both call sites can share it without duplication.
Layered on top of baseEngineConfig following the same pattern
already used for AppendSystemPrompt. When both fields are empty,
behaviour is unchanged. The use-case abstraction will eventually
subsume both fields.

Tests added (incident_test.go):

- TestProcessIncidentEvent_SlackChannelOverride: an override sets
  the per-call SLACK_CHANNEL_ID.
- TestProcessIncidentEvent_SlackChannelDefaultsToNotificationsChannel:
  with no override, the channel inherits from the first
  notifications channel (regression guard).
- TestProcessIncidentEvent_SlackTokenOverride: an override sets the
  SLACK_BOT_TOKEN SecretKeyRef and probes the override Secret for
  the well-known key.
- TestProcessIncidentEvent_SlackTokenOverrideFallsBackToTokenKey:
  override with no well-known key falls back to the literal "token".

Docs updated: docs/api/webhooks.md gains two rows in the Dispatch
configuration table and a slack_channel_id / slack_token_secret
pair in the Example configuration. CHANGELOG entry added under
Unreleased > Added.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: de59b245-d25d-40f9-a40f-116930e8f4e0

📥 Commits

Reviewing files that changed from the base of the PR and between 7c3d4aa and da0b0e9.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • docs/api/webhooks.md
  • internal/config/config.go
  • internal/controller/incident.go
  • internal/controller/incident_test.go
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • internal/controller/incident.go
  • internal/config/config.go
  • internal/controller/incident_test.go

Walkthrough

This PR adds per-incident-triage Slack routing overrides: optional config fields allow the incident-triage agent to post to a dedicated Slack channel and use a specified Kubernetes Secret for the bot token, with secret-key probing and fallback behavior.

Changes

Slack Overrides for Incident Triage Routing

Layer / File(s) Summary
Configuration schema and documentation
internal/config/config.go, CHANGELOG.md, docs/api/webhooks.md
IncidentTriageConfig gained SlackChannelID and SlackTokenSecret optional fields with YAML tags and docs. Changelog and webhook docs describe the new routing controls and the token-key probing order (SLACK_BOT_TOKEN, SLACK_TOKEN, then token).
Kubernetes Secret key resolution helper
internal/controller/controller.go
Added (*Reconciler).resolveSlackTokenKey(ctx, tokenSecret) to probe a referenced Kubernetes Secret for well-known Slack token keys and return the data key name, falling back to "token".
Incident triage Slack override application
internal/controller/incident.go
ProcessIncidentEvent applies IncidentTriage.SlackChannelID into engineCfg.Env["SLACK_CHANNEL_ID"] and uses the resolved key to populate engineCfg.SecretKeyRefs["SLACK_BOT_TOKEN"] when SlackTokenSecret is set.
Test coverage for Slack overrides
internal/controller/incident_test.go
Added incidentTestConfigWithTicketingSlack helper and four tests verifying channel override precedence, channel fallback to notifications channel, token-secret resolution when well-known key exists, and fallback to literal "token" key when not present.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • unitaryai/osmia#45: Directly related; both PRs modify ProcessIncidentEvent in internal/controller/incident.go and interact with dispatch/job creation logic.

Suggested reviewers

  • nikoul
  • htonkovac
  • dcferreira

Poem

🐇 In configs new, the channels bloom,

Tokens seek their secret room,
Overrides whisper, jobs take flight,
Shadow posts in softer light,
Hooray — the rabbit hops in delight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding per-flow Slack configuration for incident triage, which is the primary feature across config, controller, and test changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/incident-triage-slack-overrides

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Comment @coderabbitai help to get the list of available commands and usage tips.

The previous commit introduced incident_triage.slack_channel_id and
incident_triage.slack_token_secret using "override" language, which
mis-modelled the change as a special-case workaround grafted onto a
ticketing-default. They are not overrides — they are the natural
place to configure the incident flow's Slack destination, just as
notifications.channels is the natural place to configure the
ticketing flow's. Both flows are first-class; a single Osmia
deployment can run them side-by-side with separate channels and
bot tokens.

The behaviour is unchanged. This commit rewords the framing:

- IncidentTriageConfig field docstrings describe the fields as the
  incident flow's Slack config, not as overrides of a default.
- The dispatch-time comment in incident.go describes the two flows
  as peers reading from their respective YAML locations.
- Tests renamed from _SlackChannelOverride to
  _PostsToConfiguredSlackChannel, _SlackChannelDefaultsToNotificationsChannel
  to _FallsBackToTicketingSlackChannel, _SlackTokenOverride to
  _UsesConfiguredSlackTokenSecret, and
  _SlackTokenOverrideFallsBackToTokenKey to
  _FallsBackToTokenKeyForUnnamedSlackSecret. Assertion messages
  updated to match. The shared helper is renamed from
  incidentTestConfigWithDefaultSlack to
  incidentTestConfigWithTicketingSlack to reflect that the configured
  channel is the ticketing flow's, not a generic "default".
- docs/api/webhooks.md Dispatch-configuration rows reframe the
  fields as per-flow config. The example YAML in the Example
  configuration block now describes the two flows running
  side-by-side rather than overriding one with the other.
- CHANGELOG entry under [Unreleased] > Added rewritten to lead with
  "Per-flow Slack config for incident triage" rather than
  "Per-use-case Slack overrides".

The backward-compatibility behaviour is unchanged: when the
IncidentTriage Slack fields are empty, the incident flow falls back
to notifications.channels[0] (single-channel deployments keep working
without changes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sarahjanenewnham sarahjanenewnham changed the title feat(controller): add Slack channel/token overrides for incident triage feat(controller): add per-flow Slack config for incident triage May 12, 2026
@sarahjanenewnham sarahjanenewnham self-assigned this May 12, 2026
@sarahjanenewnham

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@sarahjanenewnham sarahjanenewnham merged commit b741e67 into main May 13, 2026
7 checks passed
@sarahjanenewnham sarahjanenewnham deleted the feat/incident-triage-slack-overrides branch May 13, 2026 13:43
sarahjanenewnham added a commit that referenced this pull request May 18, 2026
Add a per-flow `incident_triage.incident_io_api_key_secret` config field
that mounts `INCIDENT_IO_API_KEY` onto incident-triage agent Jobs via
SecretKeyRef. When the env var is present at job startup, setup-claude.sh
registers the incident.io remote MCP server (mcp.incident.io/mcp, HTTP
transport, Bearer-token auth) in the workspace mcp.json. When empty no
MCP server is registered — ticketing pods are unaffected.

The Bearer header is templated into mcp.json via `jq --arg` rather than
Claude Code's runtime `${VAR}` substitution, which has known bugs for
HTTP MCP headers (anthropics/claude-code#51581, #6204).

Mirrors the per-flow config precedent from #46.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sarahjanenewnham added a commit that referenced this pull request May 18, 2026
#50)

Add a per-flow `incident_triage.incident_io_api_key_secret` config field
that mounts `INCIDENT_IO_API_KEY` onto incident-triage agent Jobs via
SecretKeyRef. When the env var is present at job startup, setup-claude.sh
registers the incident.io remote MCP server (mcp.incident.io/mcp, HTTP
transport, Bearer-token auth) in the workspace mcp.json. When empty no
MCP server is registered — ticketing pods are unaffected.

The Bearer header is templated into mcp.json via `jq --arg` rather than
Claude Code's runtime `${VAR}` substitution, which has known bugs for
HTTP MCP headers (anthropics/claude-code#51581, #6204).

Mirrors the per-flow config precedent from #46.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants