Skip to content

feat: add Slack type to TaskSpawner API#35

Open
jkahuja wants to merge 2 commits intoprodfrom
AIE-17-slack-api-types
Open

feat: add Slack type to TaskSpawner API#35
jkahuja wants to merge 2 commits intoprodfrom
AIE-17-slack-api-types

Conversation

@jkahuja
Copy link
Copy Markdown

@jkahuja jkahuja commented Mar 30, 2026

What type of PR is this?

/kind feature

What this PR does / why we need it:

Adds the Slack struct to the When union type in the TaskSpawner CRD, enabling Slack as a new task source. This is the foundational schema change — no runtime behavior yet.

The Slack source uses Socket Mode (outbound WebSocket, no ingress required) and supports:

  • Optional trigger command — when empty, every non-threaded channel message triggers a task
  • Channel filtering — restrict to specific channel IDs
  • User filtering — restrict which Slack users can trigger tasks
  • Response channel override — redirect status messages to a different channel
  • Per-source poll interval — control how often accumulated events are drained

Which issue(s) this PR is related to:

Part of kelos-dev#595
Towards AIE-17

Special notes for your reviewer:

This is PR 1 of 4 for the Slack integration. Subsequent PRs will add:

  • PR 2: SlackSource implementation with Socket Mode listener
  • PR 3: Deployment builder + spawner wiring
  • PR 4: Status feedback to Slack threads

The slack-go/slack Go dependency is not added in this PR — go mod tidy removes it since nothing imports it yet. It will be added in PR 2.

Does this PR introduce a user-facing change?

Add Slack as a new TaskSpawner source type for ChatOps-driven agent execution via Socket Mode.

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 <[email protected]>
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 30, 2026

Greptile Summary

This PR adds the Slack struct to the When union type in the TaskSpawner CRD, wiring Slack Socket Mode as a new task source. It is the first of four planned PRs — no runtime behavior or controller logic is included yet.

Key changes:

  • New Slack struct with SecretRef, TriggerCommand, Channels, AllowedUsers, ResponseChannel, and PollInterval fields.
  • Autogenerated DeepCopyInto/DeepCopy for Slack (correctly handles slice fields).
  • Both CRD manifests (taskspawner-crd.yaml and install-crd.yaml) updated in sync with the Go types.

Minor findings (all P2/non-blocking):

  • Channels and AllowedUsers accept arbitrary strings — adding +kubebuilder:validation:items:Pattern markers for Slack ID formats (C…, U…) would catch typos at admission time.
  • ResponseChannel has no format constraint either.
  • The Branch/PromptTemplate doc comments do not yet document Slack template variables; these should be added before the Slack source goes live in PR 3.
  • The generated DeepCopyInto contains a redundant out.SecretRef = in.SecretRef assignment after the initial *out = *in; harmless but may indicate the file wasn't cleanly regenerated.

Confidence Score: 5/5

Safe to merge — schema-only change with correct deepcopy generation and consistent CRD manifests; all remaining findings are P2 style/quality suggestions.

No runtime behavior is introduced in this PR. The Go type definition, deepcopy, and both CRD YAML files are internally consistent. The three comments are all P2: missing ID format validation, undocumented template variables (deferred to PR 3 by design), and a redundant deepcopy line. None of these are blocking issues.

api/v1alpha1/taskspawner_types.go — consider adding channel/user ID pattern validation and documenting Slack template variables before the implementation PRs land.

Important Files Changed

Filename Overview
api/v1alpha1/taskspawner_types.go Adds Slack struct to the When union with SecretRef, TriggerCommand, Channels, AllowedUsers, ResponseChannel, and PollInterval fields; missing channel/user ID format validation and Slack template variables in Branch/PromptTemplate docs.
api/v1alpha1/zz_generated.deepcopy.go Autogenerated DeepCopy for Slack struct correctly deep-copies Channels and AllowedUsers slices; contains a redundant SecretRef re-assignment after the initial *out = *in shallow copy.
internal/manifests/charts/kelos/templates/crds/taskspawner-crd.yaml CRD YAML updated to include the slack property under when with all fields; matches Go type definition; no pattern constraints on channel/user ID arrays (consistent with Go types).
internal/manifests/install-crd.yaml install-crd.yaml updated identically to the Helm chart CRD template; diff is a clean duplicate of the chart changes.

Class Diagram

%%{init: {'theme': 'neutral'}}%%
classDiagram
    class When {
        +GitHubIssues *GitHubIssues
        +GitHubPullRequests *GitHubPullRequests
        +Cron *Cron
        +Jira *Jira
        +GitHubWebhook *GitHubWebhook
        +LinearWebhook *LinearWebhook
        +Slack *Slack
    }

    class Slack {
        +SecretRef SecretReference
        +TriggerCommand string
        +Channels []string
        +AllowedUsers []string
        +ResponseChannel string
        +PollInterval string
    }

    class SecretReference {
        +Name string
    }

    When --> Slack : "new in this PR"
    Slack --> SecretReference
Loading

Comments Outside Diff (1)

  1. api/v1alpha1/taskspawner_types.go, line 509-527 (link)

    P2 Slack template variables undocumented in Branch/PromptTemplate

    The Branch and PromptTemplate doc comments enumerate available template variables per source type (e.g. {{.Event}} for GitHub Webhook, {{.Type}} for Linear Webhook), but Slack is not listed. Users configuring a Slack-triggered spawner will have no indication of which variables (e.g. {{.Channel}}, {{.User}}, {{.Text}}) will be available.

    This is understandable given that the implementation lands in PR 3, but the variables should be added here before PR 3 ships to keep the schema docs accurate at first availability.

Reviews (1): Last reviewed commit: "feat: add Slack type to TaskSpawner API" | Re-trigger Greptile

return out
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Redundant SecretRef assignment in DeepCopyInto

The line out.SecretRef = in.SecretRef on line 645 is redundant. The preceding *out = *in (line 644) already performs a full shallow copy of all value-type fields, which includes SecretRef (a struct containing only a Name string). The second assignment is harmless since SecretReference contains no pointer or slice fields, but it adds noise to generated code that's typically expected to be minimal.

If this file is truly autogenerated by controller-gen, regenerating it after updating the type should produce the canonical form. If the redundancy is intentional (e.g., the generator explicitly re-assigns embedded structs), this can be ignored.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is auto-generated by controller-gen — we don't hand-edit it. The redundant assignment is how the generator handles embedded structs; every other source type in this file has the same pattern.

Comment on lines +427 to +437
// Channels optionally restricts which Slack channels the bot listens in.
// Values are channel IDs (e.g., "C0123456789"). When empty, the bot
// listens in every channel it has been invited to.
// +optional
Channels []string `json:"channels,omitempty"`

// AllowedUsers optionally restricts which Slack users can trigger tasks.
// Values are Slack user IDs (e.g., "U0123456789"). When empty, any user
// in the channel can trigger tasks.
// +optional
AllowedUsers []string `json:"allowedUsers,omitempty"`
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 No item-level validation on channel/user ID fields

Channels and AllowedUsers accept any strings as Slack IDs, but there is no validation constraining the format. Slack channel IDs always start with C (e.g. C0123456789) and user IDs always start with U (e.g. U0123456789). Empty strings in either list would pass validation today and silently produce unexpected filtering behavior at runtime (PR 2).

Consider adding +kubebuilder:validation:items:Pattern markers to catch obviously invalid IDs at admission time:

// +kubebuilder:validation:items:Pattern=`^C[A-Z0-9]+$`
Channels []string `json:"channels,omitempty"`

// +kubebuilder:validation:items:Pattern=`^U[A-Z0-9]+$`
AllowedUsers []string `json:"allowedUsers,omitempty"`

Similarly, ResponseChannel at line 443 could carry +kubebuilder:validation:Pattern=^C[A-Z0-9]+$ to guard against typos.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipping ID format validation for now — Slack IDs have varied prefixes beyond just C and U (e.g., G for group channels, D for DM channels, W for enterprise users). A regex like ^C[A-Z0-9]+$ would reject valid configs. We'll validate at runtime with clear error messages instead.

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

Co-Authored-By: Claude Opus 4.6 <[email protected]>
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.

1 participant