-
Notifications
You must be signed in to change notification settings - Fork 12
Description
🤖 Kelos Strategist Agent @gjkim42
Area: Integration Opportunities
Summary
GitHub Discussions is a first-class feature used by thousands of projects for Q&A, feature proposals, RFCs, and community engagement. Kelos currently supports githubIssues, githubPullRequests, cron, and jira as TaskSpawner sources, but has no way to discover work items from GitHub Discussions. This proposal adds a githubDiscussions source type that enables agents to automate community support, triage feature requests, and analyze RFCs — addressing one of the most time-consuming aspects of open-source maintenance.
Problem
1. Community Q&A is a massive time sink for maintainers
Large open-source projects receive hundreds of discussion questions monthly. Popular projects like Next.js (~18k discussions), React (~4k discussions), and VS Code (~8k discussions) rely on GitHub Discussions as their primary support channel. Maintainers spend significant time answering repetitive questions that could be answered by searching the codebase and documentation.
Unlike GitHub Issues (which represent tracked work), Discussions are conversational and often require knowledge lookup rather than code changes — making them ideal for AI agent assistance.
2. Feature request triage is inconsistent and delayed
Many projects use GitHub Discussions for feature proposals (the "Ideas" category). These proposals often sit unanswered for weeks because maintainers lack time to:
- Assess technical feasibility
- Identify affected code areas
- Estimate implementation scope
- Check for duplicate requests
An agent could perform this initial analysis immediately, giving maintainers structured information for prioritization.
3. No existing Kelos source covers this
githubIssuesuses the REST Issues API — Discussions are a completely separate API (GraphQL-only)- Discussions have unique semantics: categories (Q&A, Ideas, General), accepted answers, threaded replies, upvote counts
- The
types: ["issues", "pulls"]filter ongithubIssuescannot include discussions - Generic webhooks (Integration: Add generic webhook source type to TaskSpawner for universal event-driven task triggering #687), when implemented, could receive Discussion events but lack the polling/discovery model and structured metadata that a dedicated source provides
Proposed API
Add a githubDiscussions field to the When struct in api/v1alpha1/taskspawner_types.go:
type When struct {
GitHubIssues *GitHubIssues `json:"githubIssues,omitempty"`
GitHubPullRequests *GitHubPullRequests `json:"githubPullRequests,omitempty"`
Cron *Cron `json:"cron,omitempty"`
Jira *Jira `json:"jira,omitempty"`
GitHubDiscussions *GitHubDiscussions `json:"githubDiscussions,omitempty"` // NEW
}
// GitHubDiscussions discovers discussions from a GitHub repository.
// Uses the GitHub GraphQL API (Discussions are not available via REST).
// Authentication is provided via the workspace's secretRef.
type GitHubDiscussions struct {
// Repo optionally overrides the repository to poll for discussions,
// in "owner/repo" format. When empty, derived from workspace repo URL.
// +optional
Repo string `json:"repo,omitempty"`
// Category filters discussions by category name (e.g., "Q&A", "Ideas",
// "General"). When empty, all categories are included.
// +optional
Category string `json:"category,omitempty"`
// Answered filters Q&A discussions by answer state.
// "unanswered" returns only discussions without an accepted answer.
// "answered" returns only discussions with an accepted answer.
// "any" returns all discussions regardless of answer state.
// Only meaningful for Q&A category discussions.
// +kubebuilder:validation:Enum=unanswered;answered;any
// +kubebuilder:default=any
// +optional
Answered string `json:"answered,omitempty"`
// Labels filters discussions by labels.
// +optional
Labels []string `json:"labels,omitempty"`
// ExcludeLabels filters out discussions that have any of these labels.
// +optional
ExcludeLabels []string `json:"excludeLabels,omitempty"`
// CommentPolicy configures comment-based workflow control.
// +optional
CommentPolicy *GitHubCommentPolicy `json:"commentPolicy,omitempty"`
// Reporting configures status reporting back to the discussion.
// +optional
Reporting *GitHubReporting `json:"reporting,omitempty"`
// PollInterval overrides spec.pollInterval for this source.
// +optional
PollInterval string `json:"pollInterval,omitempty"`
}WorkItem mapping
GitHub Discussion fields map to the existing WorkItem struct:
| WorkItem field | Discussion value |
|---|---|
ID |
Discussion node ID (GraphQL global ID) |
Number |
Discussion number |
Title |
Discussion title |
Body |
Discussion body |
URL |
Discussion HTML URL |
Labels |
Discussion labels |
Comments |
Concatenated discussion comments (threaded) |
Kind |
"Discussion" (new value) |
PromptTemplate variables
All existing template variables work naturally:
promptTemplate: |
Discussion #{{.Number}}: {{.Title}}
Category: Q&A
Question:
{{.Body}}
Existing replies:
{{.Comments}}
Search the codebase and documentation to answer this question.
Post your answer as a reply on the discussion: {{.URL}}Implementation
New source: internal/source/github_discussions.go
Unlike the existing GitHub sources (which use REST), this source uses the GitHub GraphQL API because Discussions are only available via GraphQL:
type GitHubDiscussionsSource struct {
Owner string
Repo string
Category string
Answered string // "unanswered", "answered", "any"
Labels []string
ExcludeLabels []string
Token string
BaseURL string // For GitHub Enterprise
Client *http.Client
TriggerComment string
ExcludeComments []string
// ... other comment policy fields
}
func (s *GitHubDiscussionsSource) Discover(ctx context.Context) ([]WorkItem, error) {
// 1. Query discussions via GraphQL
// 2. Filter by category, labels, answered state
// 3. Apply comment policy (trigger/exclude)
// 4. Map to WorkItem structs
}GraphQL query structure
query($owner: String!, $repo: String!, $categoryId: ID, $after: String) {
repository(owner: $owner, name: $repo) {
discussions(
first: 25
after: $after
categoryId: $categoryId
orderBy: { field: UPDATED_AT, direction: DESC }
) {
nodes {
id
number
title
body
url
isAnswered
labels(first: 10) { nodes { name } }
comments(first: 20) {
nodes {
body
createdAt
author { login }
isAnswer
}
}
}
pageInfo { hasNextPage endCursor }
}
}
}The category ID is resolved with a separate query on first use:
query($owner: String!, $repo: String!) {
repository(owner: $owner, name: $repo) {
discussionCategories(first: 25) {
nodes { id name }
}
}
}Changes to spawner
In cmd/kelos-spawner/main.go, extend buildSource():
if ts.Spec.When.GitHubDiscussions != nil {
gd := ts.Spec.When.GitHubDiscussions
token, err := readGitHubToken(tokenFile)
if err != nil {
return nil, err
}
commentPolicy, err := resolveGitHubCommentPolicy(gd.CommentPolicy, "", nil)
if err != nil {
return nil, err
}
return &source.GitHubDiscussionsSource{
Owner: owner,
Repo: repo,
Category: gd.Category,
Answered: gd.Answered,
Labels: gd.Labels,
ExcludeLabels: gd.ExcludeLabels,
Token: token,
BaseURL: apiBaseURL,
Client: httpClient,
TriggerComment: commentPolicy.TriggerComment,
ExcludeComments: commentPolicy.ExcludeComments,
// ... other comment policy fields
}, nil
}Changes to controller
In internal/controller/taskspawner_controller.go and taskspawner_deployment_builder.go, extend the source detection logic to recognize githubDiscussions as a polling-based source (creates a Deployment, same as githubIssues). The controller changes are minimal — the existing patterns for deriving --github-owner and --github-repo from the workspace apply directly.
Reporting
GitHub Discussions support comments via GraphQL mutation (addDiscussionComment). The existing GitHubReporting mechanism can be extended to post status comments on discussions, following the same pattern as issue/PR reporting in internal/reporting/github.go.
Example TaskSpawner configurations
1. Automated Q&A responder
apiVersion: kelos.dev/v1alpha1
kind: TaskSpawner
metadata:
name: qa-responder
spec:
when:
githubDiscussions:
category: "Q&A"
answered: unanswered
excludeLabels: ["no-bot"]
reporting:
enabled: true
pollInterval: "10m"
maxConcurrency: 2
taskTemplate:
type: claude-code
credentials:
type: oauth
secretRef:
name: claude-creds
workspaceRef:
name: my-project
branch: "main" # Read-only, no branch creation needed
promptTemplate: |
A user asked a question in GitHub Discussion #{{.Number}}: {{.Title}}
Question:
{{.Body}}
Existing replies (if any):
{{.Comments}}
Search the codebase, documentation, and existing discussions to find
the answer. Then post a helpful reply to the discussion at {{.URL}}
using `gh api graphql`.
If you cannot find a definitive answer, say so honestly and suggest
where the user might find help.
ttlSecondsAfterFinished: 36002. Feature request analyzer
apiVersion: kelos.dev/v1alpha1
kind: TaskSpawner
metadata:
name: feature-analyzer
spec:
when:
githubDiscussions:
category: "Ideas"
labels: ["needs-analysis"]
commentPolicy:
triggerComment: "/analyze"
allowedUsers: ["maintainer1", "maintainer2"]
pollInterval: "15m"
maxConcurrency: 1
taskTemplate:
type: claude-code
credentials:
type: oauth
secretRef:
name: claude-creds
workspaceRef:
name: my-project
promptTemplate: |
Analyze this feature request from Discussion #{{.Number}}: {{.Title}}
Proposal:
{{.Body}}
Community feedback:
{{.Comments}}
Perform the following analysis and post a structured reply:
1. **Feasibility**: Search the codebase to assess how this could be implemented
2. **Affected areas**: List files and modules that would need changes
3. **Scope estimate**: Small (1-2 files), Medium (3-10 files), or Large (10+ files)
4. **Related work**: Check for similar existing functionality or past proposals
5. **Recommendation**: Suggest next steps (create issue, needs RFC, already possible, etc.)
ttlSecondsAfterFinished: 36003. RFC impact analysis
apiVersion: kelos.dev/v1alpha1
kind: TaskSpawner
metadata:
name: rfc-analyzer
spec:
when:
githubDiscussions:
category: "RFCs"
labels: ["rfc"]
commentPolicy:
triggerComment: "/impact"
pollInterval: "30m"
maxConcurrency: 1
taskTemplate:
type: claude-code
credentials:
type: oauth
secretRef:
name: claude-creds
workspaceRef:
name: my-project
promptTemplate: |
Perform an impact analysis for RFC #{{.Number}}: {{.Title}}
RFC content:
{{.Body}}
Discussion:
{{.Comments}}
Analyze the codebase and produce:
1. List of files/packages that would be affected
2. API surface changes (breaking vs non-breaking)
3. Test coverage gaps for affected areas
4. Migration considerations for existing users
5. Dependencies that may be impacted
Post the analysis as a reply to the discussion at {{.URL}}.
ttlSecondsAfterFinished: 7200Technical considerations
GraphQL vs REST
This is the first Kelos source to require GraphQL. Design choices:
- No external GraphQL library: Use raw HTTP POST requests with JSON-encoded queries (same pattern as the Jira source, which also uses POST requests). This avoids adding a new dependency.
- Shared HTTP client: Reuse the existing ETag-aware HTTP client. GitHub's GraphQL API doesn't support ETag caching, but the transport gracefully handles this (no conditional request, full response returned).
- Rate limiting: GitHub GraphQL API has a separate rate limit (5,000 points/hour) from REST (5,000 requests/hour). A single discussions query costs ~1-2 points. At the default 10-minute poll interval, a spawner uses ~10-20 points/hour — well within limits.
GitHub Enterprise compatibility
GitHub Enterprise Server supports GraphQL and Discussions (since GHES 3.7). The baseURL flag is already passed to all sources. For GraphQL, the endpoint is {baseURL}/graphql instead of {baseURL}/api/graphql. The source should detect and handle both patterns.
Scope
| File | Change |
|---|---|
api/v1alpha1/taskspawner_types.go |
Add GitHubDiscussions struct and field to When |
api/v1alpha1/zz_generated.deepcopy.go |
make update regenerates |
internal/source/github_discussions.go |
New source implementation (~200 lines) |
internal/source/github_discussions_test.go |
Unit tests for discovery, filtering, comment policy |
cmd/kelos-spawner/main.go |
Add githubDiscussions branch to buildSource() |
internal/controller/taskspawner_controller.go |
Recognize githubDiscussions as polling-based source |
internal/controller/taskspawner_deployment_builder.go |
Pass flags for discussions source |
internal/reporting/github.go |
Add GraphQL comment mutation for discussion reporting |
| CRD manifests | make update to regenerate |
docs/reference.md |
Document new source type |
No new dependencies needed. No changes to the Task CRD, Workspace CRD, or AgentConfig CRD.
Backward compatibility
- Purely additive: new optional field on
When - Existing TaskSpawners are unaffected
- No migration needed
- Uses the same workspace auth pattern as other GitHub sources
Why this matters for Kelos adoption
- Large addressable market: GitHub reports 400M+ discussion comments across the platform. Projects with active Discussions face the same automation gap that Kelos already solves for Issues and PRs.
- Low barrier to entry: Teams already using Kelos with
githubIssuescan add a Discussions spawner with the same workspace and credentials — zero new infrastructure. - Unique positioning: No competing Kubernetes-native agent framework offers Discussion-driven automation. This differentiates Kelos from tools like Copilot Workspace or Devin, which focus exclusively on code changes.
- Natural upgrade path: Teams start with Q&A automation (low risk, high visibility), then expand to feature request analysis and RFC processing — deepening their investment in Kelos.
Related issues
- New use case: Reusable open-source maintainer automation kit with example TaskSpawner configs #568 — Reusable OSS maintainer automation kit (Discussions source would be a key building block)
- Integration: Add generic webhook source type to TaskSpawner for universal event-driven task triggering #687 — Generic webhooks (complementary: webhooks handle push events, this handles polling discovery)
- Integration: Add GitLab issues and merge requests as TaskSpawner source types for multi-platform agent workflows #701 — GitLab support (similar multi-platform expansion pattern)
- API: Add onCompletion notification hooks to TaskSpawner for outbound event delivery on task terminal phases #749 — onCompletion hooks (could notify maintainers when a Discussion response is posted)