Skip to content

feat(slack): add channelTypes filter for DM-specific spawners - #102

Draft
anomalogravity[bot] wants to merge 65 commits into
prodfrom
feat/channel-type-filter
Draft

feat(slack): add channelTypes filter for DM-specific spawners#102
anomalogravity[bot] wants to merge 65 commits into
prodfrom
feat/channel-type-filter

Conversation

@anomalogravity

Copy link
Copy Markdown

Summary

  • Adds a new channelTypes field to the Slack TaskSpawner config that filters by Slack channel type (channel, group, im, mim)
  • Enables DM-specific spawners that fire without requiring @-mentions — users can just type in a DM
  • For slash commands, maps Slack's channelName: "directmessage" to "im" for consistent filtering
  • Empty channelTypes list matches all channel types (fully backward compatible)

Context

Companion to https://github.com/datagravity-ai/dquality/pull/27735, which adds the DM spawner YAML configs referencing this field. The config PR is inert until this Kelos change lands.

Changes

File What
api/v1alpha1/taskspawner_types.go Add ChannelTypes []string field to Slack struct
api/v1alpha1/zz_generated.deepcopy.go Add deepcopy for the new field
internal/slack/filter.go Add ChannelType to SlackMessageData, add matchesChannelType helper, integrate into MatchesSpawner
internal/slack/handler.go Populate ChannelType from message events and slash commands
internal/slack/filter_test.go Add unit tests for matchesChannelType, MatchesSpawner with channelTypes, and full DM routing scenario

Test plan

  • All existing tests pass (no regressions)
  • New TestMatchesChannelType unit tests cover match/reject/empty-list cases
  • New TestDMRouting integration test validates DM vs channel spawner routing
  • Full go build ./... succeeds
  • Deploy to dev and verify DM messages route to DM spawners only
  • Verify channel messages still require @-mention and don't hit DM spawners

🤖 Generated with Claude Code

knechtionscoding and others added 30 commits April 17, 2026 11:00
* feat: add github and linear webhook events

* fix: build images

* Fixed Issues
1. P1 - MaxConcurrencyError HTTP Response ✅
Fixed ServeHTTP to properly handle MaxConcurrencyError with HTTP 503 and Retry-After header
Previously returned generic HTTP 500, now correctly returns 503 for rate limiting
2. P1 - Empty APIVersion/Kind in Owner References ✅
Fixed owner reference creation to use client.Scheme().ObjectKinds() to get proper GVK
Added missing Controller and BlockOwnerDeletion fields for proper garbage collection
Previously had empty strings, now has correct API version and kind
3. P1 - TOCTOU Race Condition in Idempotency ✅
Replaced separate IsProcessed()/MarkProcessed() with atomic CheckAndMark()
Eliminated race window where two requests with same delivery ID could both pass the check
Now uses single lock operation for check-and-set
4. P2 - Invalid Kubernetes Names from Truncation ✅
Fixed task name generation to handle nil/empty ID values safely
Added strings.TrimRight(taskName[:63], "-.") to ensure names don't end with invalid characters
Prevents server-side validation failures
5. P2 - BodyContains Filter Ignored for IssuesEvent ✅
Fixed GitHub filter to check issue body for BodyContains on IssuesEvent
Previously only checked comment body on IssueCommentEvent
Now properly filters by issue body content
All tests pass and the implementation builds successfully. The webhook system now properly handles rate limiting, ensures atomic idempotency checks, creates valid owner references for garbage collection, generates valid Kubernetes resource names, and correctly applies all filter conditions.

* chore: generate
* Add HTTP timeouts to webhook server to prevent resource exhaustion attacks
  - ReadTimeout: 30s, WriteTimeout: 30s, ReadHeaderTimeout: 10s, IdleTimeout: 120s
  - Protects against Slowloris and connection holding attacks

* Add ownership verification before deleting stale resources
  - Prevents webhook reconciliation from deleting unrelated Deployments/CronJobs
  - Only deletes resources owned by TaskSpawners with proper controller reference

* Enable strict template validation with missingkey=error
  - Missing template variables now surface as errors instead of silent "<no value>"
  - Prevents malformed prompts/labels/annotations from invalid templates
  - Applied to both taskbuilder and source template rendering

* Update controller tests with proper owner references for stale resource scenarios

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add Slack type to TaskSpawner API

Add Slack as a new source type in the When struct, enabling
ChatOps-driven agent execution from Slack messages via Socket Mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: remove ResponseChannel from Slack type

Bot responses will always be posted as thread replies to the
originating message. No override is needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: implement SlackSource with Socket Mode listener (#36)

* feat: implement SlackSource with Socket Mode listener

Add SlackSource that connects to Slack via Socket Mode and accumulates
WorkItems from channel messages. Discover() drains the queue each cycle.

Event processing logic (shouldProcess, matchesChannel, matchesUser,
buildWorkItem) is extracted as pure functions for testability. The Socket
Mode goroutine is a thin glue layer.

Filtering: ignores threaded replies, bot messages, and self-messages.
Optional trigger command prefix support with stripping.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address review feedback for SlackSource

- Replace started bool with sync.Once to fix potential data race
- Use context.Background() for Socket Mode goroutine lifetime
- Add 5-second timeout for enrichment API calls
- Use composite key for slash command work item ID
- Filter message_changed, message_deleted, message_replied subtypes
- Add test cases for new filtered subtypes

Towards AIE-17

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: guard s.cancel with mutex to prevent data race in Stop()

s.cancel is written in Start() and read in Stop() — without
synchronization this is a race if Stop() is called while Start()
is still running inside startOnce.Do.

Towards AIE-17

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: wire Slack source into deployment builder and spawner (#37)

* feat: wire Slack source into deployment builder and spawner

- Add Slack block to buildPodParts() mounting SLACK_BOT_TOKEN and
  SLACK_APP_TOKEN from SecretRef, passing optional CLI args for
  trigger command, channels, and allowed users
- Add CLI flags and spawnerRuntimeConfig fields for Slack config
- Add Slack case to buildSource() and resolvedPollInterval()
- Add parseCSV helper for comma-separated CLI flag values
- Add TestDeploymentBuilder_Slack and TestDeploymentBuilder_SlackMinimal

Towards AIE-17

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add TestBuildSource_Slack for buildSource coverage

Verifies the Slack branch in buildSource() correctly reads env vars
for bot/app tokens and parses CSV CLI args for trigger command,
channels, and allowed users.

Towards AIE-17

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add Slack status feedback as thread replies (#38)

* feat: add Slack status feedback as thread replies

When a Slack message triggers a Task, the bot now posts thread replies
to the originating message as the Task progresses:
- "Working on your request..." when accepted
- "Done!" when succeeded
- "Failed." when failed

Implementation:
- SlackReporter posts/updates thread replies via slack-go
- SlackTaskReporter mirrors GitHubTaskReporter pattern using
  Slack-specific annotations for channel, thread_ts, reply_ts
- sourceAnnotations() stamps Slack metadata on Tasks at creation
- reportingEnabled() returns true for Slack sources (always-on)
- buildWorkItem() now carries channel ID for annotation stamping

Towards AIE-17

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: enrich Slack status messages and address review feedback

- Include PR URL in succeeded messages and error details in failed messages
- Extract SlackMessenger interface for testability (enables fake injection)
- Reuse slack.Client instead of creating one per API call
- Validate SLACK_BOT_TOKEN is set before constructing reporter
- Add full post+persist and update path tests for SlackTaskReporter

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: skip thread_ts annotation for slash command work items

Slash command item IDs are compound strings (e.g. "C123:/cmd:trigger")
that are not valid Slack message timestamps. Using them as thread_ts
would cause PostThreadReply to fail with invalid_arguments. Only set
the annotation when the ID is a valid Slack timestamp (digits.digits).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs: move pollInterval to per-source form in README snippets

The README YAML snippets used the deprecated top-level
spec.pollInterval field. Move pollInterval inside the
githubIssues source block in both the self-development
section and the TaskSpawner quick-start example, aligning
them with the reference docs, examples, and the actual
kelos-workers.yaml manifest.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update opencode image to 1.3.9

* Update claude-code image to 2.1.88

* Update cursor image to 2026.03.30-a5d3e17

* feat: log cache misses in ghproxy

* fix: use fetch-depth 2 for fork e2e merge ref checkout

Shallow clone with depth 1 prevents git from reporting parent SHAs
of the merge commit, causing the head SHA validation to always fail.

* feat: add github webhook events (#34)

* feat: add github webhook events

* fix: build images

* Fixed Issues
1. P1 - MaxConcurrencyError HTTP Response ✅
Fixed ServeHTTP to properly handle MaxConcurrencyError with HTTP 503 and Retry-After header
Previously returned generic HTTP 500, now correctly returns 503 for rate limiting
2. P1 - Empty APIVersion/Kind in Owner References ✅
Fixed owner reference creation to use client.Scheme().ObjectKinds() to get proper GVK
Added missing Controller and BlockOwnerDeletion fields for proper garbage collection
Previously had empty strings, now has correct API version and kind
3. P1 - TOCTOU Race Condition in Idempotency ✅
Replaced separate IsProcessed()/MarkProcessed() with atomic CheckAndMark()
Eliminated race window where two requests with same delivery ID could both pass the check
Now uses single lock operation for check-and-set
4. P2 - Invalid Kubernetes Names from Truncation ✅
Fixed task name generation to handle nil/empty ID values safely
Added strings.TrimRight(taskName[:63], "-.") to ensure names don't end with invalid characters
Prevents server-side validation failures
5. P2 - BodyContains Filter Ignored for IssuesEvent ✅
Fixed GitHub filter to check issue body for BodyContains on IssuesEvent
Previously only checked comment body on IssueCommentEvent
Now properly filters by issue body content
All tests pass and the implementation builds successfully. The webhook system now properly handles rate limiting, ensures atomic idempotency checks, creates valid owner references for garbage collection, generates valid Kubernetes resource names, and correctly applies all filter conditions.

* chore: generate
* fix: normalize the tasknames

* feat: scope ghproxy by workspace with own auth

Replace the centralized static ghproxy with per-workspace instances
managed by a new WorkspaceReconciler. Each workspace ghproxy owns
its GitHub auth directly via token-refresher sidecar, fixing the
ETag-per-token cache invalidation cycle caused by multiple spawner
pods using different tokens through a shared proxy.

Also logs cache misses with the cache key for observability.

* fix: populate SpawnerRef fields for Task owner references

PR kelos-dev#851 introduced taskbuilder.BuildTask with SpawnerRef but the
spawner call site only set Name, leaving APIVersion, Kind, and UID
empty. Kubernetes rejects owner references with empty required fields,
causing Task creation to fail.

Also remove BlockOwnerDeletion since neither the spawner nor webhook
RBAC role has the permissions Kubernetes requires for that field.

* Merge upstream prod into fork: add GitHub webhook improvements, Linear webhooks, Slack support

Merges upstream GitHub webhook events and Linear webhook support into the
fork which already has Slack integration. All three webhook sources
(GitHub, Linear, Slack) are now supported alongside cron and poll-based
task spawners. Resolves all merge conflicts across 21 files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Gunju Kim <gjkim042@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Fix SlackSource being rebuilt every poll cycle

* Add test

* Use CRD source type instead of env vars

* Add retry with backoff for TaskSpawner CRD fetch at startup

* Fix test
Add when.linearWebhook source type that triggers task creation from Linear webhook events. Includes HMAC-SHA256 signature validation, filtering by resource type, action, workflow state, and labels, and Helm chart support for the Linear webhook server alongside GitHub.
* feat: handle slack responses properly

* fix: review comments
* docs: add contributing guide and recommended steps

* Update ANOMALO-CONTRIBUTING-GUIDE.md

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* feat: Add support for fileChanged Patterns

* Update internal/webhook/handler.go

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix: review comments

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: improve Slack thread interactivity for gravity agent

Thread replies were silently dropped by shouldProcess(), preventing
follow-up conversations. Now thread replies create new tasks with the
full thread history as context via GetConversationRepliesContext.

Status messages were edited in-place, causing confusing "(edited)" tags.
Now each phase change posts a new thread reply instead of updating the
existing one.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: only process thread replies where bot participated

Address greptile review feedback:
- Add botParticipated() check so thread replies in unrelated
  conversations don't spawn tasks
- Use a separate 10s timeout for GetConversationRepliesContext
  instead of sharing the 5s enrichCtx across all API calls

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: use Slack Block Kit for rich message formatting

Replace plain text Slack replies with Block Kit blocks for a more
human-friendly experience. Status messages now use emoji headers,
structured sections for agent responses and PR links, and a muted
context block for the task name. Fallback text is preserved for
notifications and accessibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Address Greptile comments

* Fix codestyle

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Linear Comment webhooks include data.issue.id/title/url but not
data.issue.description. This causes Go template rendering to crash
with "map has no entry for key description" when spawner prompt
templates reference {{.Payload.data.issue.description}}.

Extend the existing label enrichment GraphQL query to also fetch the
issue description, and make enrichment unconditional for all Comment
events (not gated on spawner filter needs). This prevents a class of
template errors cheaply with a single API call that fetches both
labels and description.

Co-authored-by: Hans Knecht <hans@anomalo.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Docs: add commentPolicy, reporting, and excludeAuthors to reference
…56)

#### What type of PR is this?

/kind bug

#### What this PR does / why we need it:

Slack's `mrkdwn` text format does not support markdown tables, lists, or headers. When an agent response contains these, they render as garbled plain text in Slack messages.

This PR adds a markdown-to-Block Kit parser (`responseToBlocks`) that splits the decoded agent response into segments and converts each to the appropriate Slack block type:

- Markdown tables (`| col | col |`) become `TableBlock` with `RichTextBlock` cells
- Unordered lists (`- item`, `* item`) and ordered lists (`1. item`) become `RichTextBlock` with `RichTextList`
- Headers (`#`, `##`, `###`) become `HeaderBlock`
- Plain text remains a `SectionBlock` with `mrkdwn` (unchanged behavior)

`FormatSlackSucceeded` and `FormatSlackFailed` now use `responseToBlocks()` instead of placing the entire response into a single `SectionBlock`.

#### Which issue(s) this PR is related to:

N/A

#### Special notes for your reviewer:

- All block types use the existing `slack-go/slack` v0.20.0 library (`TableBlock`, `RichTextBlock`, `HeaderBlock`) -- no custom structs needed.
- The parser handles edge cases: `*bold*` is not falsely matched as a list item, `3.14` is not matched as an ordered list, and pipe characters in plain text don't become tables unless followed by a separator row.
- Slack limits tables to 100 rows and 1 table per message. We don't enforce this client-side -- invalid input will surface as a Slack API error.
- Nested/indented lists are not supported; all list items must be at the same level.

#### Does this PR introduce a user-facing change?

```release-note
Slack messages now render markdown tables, lists, and headers using native Block Kit blocks instead of raw text.
```
#### What type of PR is this?

/kind bug

#### What this PR does / why we need it:

Fixes `invalid_blocks` error from the Slack API that prevented all Slack messages from being posted.

The `tableBlock()` function in `slack_blocks.go` was setting `column_settings` with an empty `align` field (`"align": ""`). Slack only accepts `"left"`, `"center"`, or `"right"` — an empty string causes the entire message to be rejected with `invalid_blocks`. Since `column_settings` is optional, the fix removes it entirely.

#### Which issue(s) this PR is related to:

N/A

#### Special notes for your reviewer:

- Root cause: `slack.ColumnSetting{IsWrapped: true}` leaves `Align` as its zero value (`""`), which `encoding/json` serializes as `"align": ""`. The Slack API rejects this.
- The `slack-go` library's own `TableBlock` test also uses no `column_settings`, confirming they're optional.
- The error was retried every ~10 seconds per reconcile loop, visible in spawner logs as `posting Slack thread reply: invalid_blocks`.

#### Does this PR introduce a user-facing change?

```release-note
Fix Slack message posting failure caused by invalid table block column settings.
```
…Spawner` deployments (#51)

/kind feature

Introduces a centralized `kelos-slack-server` binary that replaces the per-`TaskSpawner` Slack deployment model. Instead of each `TaskSpawner` with a `spec.when.slack` source running its own spawner pod with a dedicated Socket Mode WebSocket connection, a single server now handles all Slack events and routes them to matching `TaskSpawners` via the Kubernetes API — following the same pattern as `kelos-webhook-github` and `kelos-webhook-linear`.

**Why this matters:**
- Slack App Tokens support only one concurrent Socket Mode connection. The per-`TaskSpawner` model breaks when multiple `TaskSpawners` share the same Slack app.
- Reduces resource overhead — one pod instead of N pods for N Slack-enabled `TaskSpawners`.
- Aligns Slack with the existing webhook architecture, making the codebase more consistent.

**What's included:**

| Path | Description |
|------|-------------|
| `cmd/kelos-slack-server/main.go` | Server binary using `controller-runtime` for health probes, metrics, leader election, and a reporting loop |
| `internal/slack/handler.go` | Central `SlackHandler` — connects via Socket Mode, dispatches `EventsAPI` and `SlashCommand` events, routes messages to matching `TaskSpawners` |
| `internal/slack/filter.go` | `SlackMessageData` struct, channel/user matching (`MatchesSpawner`), trigger command processing (`ProcessTriggerCommand`), and template variable extraction (`ExtractSlackWorkItem`) |
| `internal/slack/thread.go` | Thread context fetching (`FetchThreadContext`), bot participation check (`BotParticipated`), and conversation formatting (`FormatThreadContext`) |
| `internal/slack/filter_test.go` | Tests for matching, trigger processing, work item extraction, and `shouldProcess` |
| `internal/slack/thread_test.go` | Tests for `BotParticipated` and `FormatThreadContext` |
| `internal/controller/taskspawner_controller.go` | Added `Slack` to `isWebhookBased` so the controller skips spawner-pod creation for Slack `TaskSpawners` |
| `api/v1alpha1/taskspawner_types.go` | Removed `SecretRef` and `PollInterval` from `Slack` struct — these are now server-side concerns |
| `internal/controller/taskspawner_deployment_builder.go` | Removed Slack args/env injection from `buildPodParts` |
| `cmd/kelos-spawner/main.go` | Removed all Slack flags, persistent source, reporting, and `buildSourceWithProxy` Slack branch |
| `cmd/kelos-spawner/reconciler.go` | Removed Slack config fields, reporting branch, and poll interval case |
| `internal/source/slack.go` | Deleted — per-`TaskSpawner` Slack source is no longer needed |
| `internal/source/slack_test.go` | Deleted |
| `internal/manifests/charts/kelos/templates/slack-server.yaml` | Helm template for the centralized `kelos-slack-server` `Deployment` |
| `internal/manifests/charts/kelos/templates/rbac.yaml` | Added `kelos-slack-server-role` `ClusterRole` and binding |
| `internal/manifests/charts/kelos/templates/serviceaccount.yaml` | Added `kelos-slack-server` `ServiceAccount` |
| `internal/manifests/charts/kelos/values.yaml` | Added `slackServer` values (`enabled`, `replicas`, `secretName`, `image`, `resources`) |
| `internal/manifests/charts/kelos/templates/crds/taskspawner-crd.yaml` | Removed `secretRef` and `pollInterval` from Slack schema, updated description |
| `internal/manifests/install-crd.yaml` | Same CRD changes as above (standalone install manifest) |

**Message routing flow:**
1. Socket Mode receives a Slack event
2. `handleEventsAPI` / `handleSlashCommand` parse and enrich the message
3. `routeMessage` lists all `TaskSpawners` with `spec.when.slack` set
4. For each spawner: checks suspended state, `maxConcurrency`, channel/user filters, and trigger command
5. Creates a `Task` with Slack reporting annotations (`kelos.dev/slack-reporting`, `kelos.dev/slack-channel`, `kelos.dev/slack-thread-ts`)

N/A

- The reporting loop in `main.go` lists **all** `Tasks` cluster-wide (not scoped to a single `TaskSpawner`), matching the centralized model. Each `Task`'s Slack annotations determine whether reporting applies.
- Thread interactivity: thread replies only trigger tasks if the bot has previously participated in the thread (`BotParticipated` check), preventing noise from unrelated conversations.
- The Helm chart follows the same pattern as `webhook-server.yaml` — `Deployment` with `ServiceAccount`, `ClusterRole`, and `ClusterRoleBinding`. The Slack server additionally needs `update` on `tasks` (for reporting annotations) and `leases`/`events` (for leader election).

```release-note
Add centralized `kelos-slack-server` for routing Slack messages to matching `TaskSpawners`, replacing the per-`TaskSpawner` Socket Mode deployment model. Remove `secretRef` and `pollInterval` from the `Slack` CRD struct. Add Helm chart support via `slackServer.enabled`.
```
…erver` image (#60)

/kind bug

The `kelos-slack-server` binary was added in #59 but was missing a `Dockerfile` and wasn't included in the `IMAGE_DIRS` list in the `Makefile`. CI wouldn't build or push the `kelos-slack-server` Docker image, so it can't be deployed.

This adds:
- `cmd/kelos-slack-server/Dockerfile` (same `distroless/static:nonroot` pattern as the other server images)
- `cmd/kelos-slack-server` to `IMAGE_DIRS` in the `Makefile`

N/A

The `Dockerfile` is identical in structure to `cmd/kelos-webhook-server/Dockerfile`.

```release-note
NONE
```
anomalogravity Bot and others added 8 commits April 17, 2026 11:05
* feat: edit Slack progress messages in-place instead of posting new ones

Progress updates from agent pod logs now edit a single thread reply
rather than posting a new message each time the text changes. Status
transition messages (accepted, succeeded, failed) continue to post as
new thread replies.

Changes:
- Add UpdateThreadReply to SlackMessenger interface and SlackReporter
- Track per-task progress message timestamp in progressTS map
- First progress update posts a new reply; subsequent updates edit it
- Clean up progressTS alongside lastProgress on terminal phase and sweep

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: clear stale progressTS on update failure

When a Slack progress message is deleted externally, UpdateMessage
fails on every tick but progressTS was never cleared, preventing
fallback to posting a fresh reply. Clear the stale TS on error so
the next tick recovers by posting a new message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Gravity Bot <gravity@anomalo.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Jasmine Ahuja <jasmine@anomalo.com>
…#96)

* feat(webhook): add extraEnv to generic webhook for per-source secrets

Allow mounting individual Kubernetes secrets as env vars in the generic
webhook server pod. This supports the <SOURCE>_WEBHOOK_SECRET convention
when each source has its own secret (e.g., linear-comment-webhook-secret).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: uncomment generic webhook example in helm-values-webhook.yaml

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address review feedback on generic webhook extraEnv

Remove redundant outer `if or` guard in the Helm template — each inner
block already gates itself. Assert `key: WEBHOOK_SECRET` in the test to
catch template regressions on the secretKeyRef key field.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
#97)

* feat(deploy): enable generic webhook with Linear comment secret in dev

Wire up the generic webhook server in the dev deployment values,
injecting LINEAR_COMMENT_WEBHOOK_SECRET from the linear-comment-webhook-secret
Kubernetes Secret via extraEnv. Add rollout restart/status for the new
kelos-webhook-generic deployment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(deploy): add PodMonitoring for generic webhook server

Add a PodMonitoring resource for kelos-webhook-generic so its metrics
are scraped, consistent with the existing kelos-webhook-github monitor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add a new ChannelTypes field to the Slack spawner config that filters
by Slack channel type (channel, group, im, mim). This enables spawners
that only fire on DMs, so users can message the bot without @-mentioning
it. For slash commands, "directmessage" is mapped to "im" for
consistency. Empty list means match all (backward compatible).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants