-
Notifications
You must be signed in to change notification settings - Fork 14
API: Add metadata templates to TaskTemplate for source-aware task labeling and external integration #819
Description
🤖 Kelos Strategist Agent @gjkim42
Summary
Spawned Tasks currently receive only one hardcoded label (kelos.dev/taskspawner) and a few system annotations (kelos.dev/source-kind, kelos.dev/source-number). There is no way for users to propagate source-item metadata (e.g., issue priority labels, author, area labels) onto Task objects as custom labels or annotations. This limits observability, scheduling flexibility, and integration with external tooling.
Problem
When operating Kelos at scale with many TaskSpawners, operators need to answer questions like:
- "How many tasks are running for
priority/criticalissues right now?" - "What's the average duration of tasks triggered by team-backend vs team-frontend issues?"
- "Route tasks for
size/XLissues to high-memory node pools" - "Show me all tasks related to security vulnerabilities"
Today, none of this is possible because spawned Tasks carry no user-controllable metadata derived from the source item. The only workaround is parsing the rendered prompt text, which is fragile and not queryable via kubectl or Prometheus.
Current task creation code (cmd/kelos-spawner/main.go:319-326):
task := &kelosv1alpha1.Task{
ObjectMeta: metav1.ObjectMeta{
Name: taskName,
Namespace: ts.Namespace,
Labels: map[string]string{
"kelos.dev/taskspawner": ts.Name, // only label
},
Annotations: annotations, // only source-kind, source-number, github-reporting
},
// ...
}Proposal
Add an optional metadata field to TaskTemplate that supports Go text/template rendering (same engine used by promptTemplate and branch) for both labels and annotations:
API change in api/v1alpha1/taskspawner_types.go:
// TaskTemplateMetadata defines templated metadata for spawned Tasks.
type TaskTemplateMetadata struct {
// Labels are additional labels to set on spawned Tasks.
// Values support Go text/template variables from the work item.
// +optional
Labels map[string]string `json:"labels,omitempty"`
// Annotations are additional annotations to set on spawned Tasks.
// Values support Go text/template variables from the work item.
// +optional
Annotations map[string]string `json:"annotations,omitempty"`
}
type TaskTemplate struct {
// Metadata defines additional labels and annotations for spawned Tasks.
// Label/annotation values support the same Go text/template variables
// as promptTemplate: {{.ID}}, {{.Title}}, {{.Kind}}, {{.Number}},
// {{.Labels}}, {{.URL}}, {{.Branch}}, etc.
// User-defined labels are merged with system labels; system labels
// (kelos.dev/*) take precedence on conflict.
// +optional
Metadata *TaskTemplateMetadata `json:"metadata,omitempty"`
// ... existing fields
}Example TaskSpawner config:
apiVersion: kelos.dev/v1alpha1
kind: TaskSpawner
metadata:
name: issue-worker
spec:
when:
githubIssues:
labels: ["agent-task"]
priorityLabels: ["priority/critical", "priority/important-soon"]
taskTemplate:
metadata:
labels:
kelos.dev/source-kind: "{{.Kind}}"
kelos.dev/priority: "{{index .Labels 0}}"
team.example.com/area: "backend"
annotations:
kelos.dev/source-url: "{{.URL}}"
kelos.dev/source-title: "{{.Title}}"
type: claude-code
credentials:
type: api-key
secretRef:
name: anthropic-key
workspaceRef:
name: my-repo
promptTemplate: |
Fix the following issue: {{.Title}}
{{.Body}}Spawner code change (cmd/kelos-spawner/main.go):
// After rendering prompt and branch, render metadata templates:
taskLabels := map[string]string{
"kelos.dev/taskspawner": ts.Name,
}
if ts.Spec.TaskTemplate.Metadata != nil {
for k, v := range ts.Spec.TaskTemplate.Metadata.Labels {
rendered, err := source.RenderTemplate(v, item)
if err != nil {
log.Error(err, "rendering label template", "key", k)
continue
}
if !strings.HasPrefix(k, "kelos.dev/") || taskLabels[k] == "" {
taskLabels[k] = rendered
}
}
}Use cases enabled
| Use case | How |
|---|---|
| Priority-based node scheduling | Set kelos.dev/priority label → use with nodeAffinity in podOverrides or a future PriorityClass integration |
| Prometheus metrics segmentation | Query kelos_task_duration_seconds{kelos_dev_priority="critical"} via label-based metric relabeling |
| kubectl filtering | kubectl get tasks -l kelos.dev/priority=critical or kelos get tasks with label selectors |
| Cost allocation | Label tasks with team, project, or cost-center for chargeback via Kubecost/OpenCost |
| External dashboard integration | Grafana/Datadog can read task labels for filtering and grouping |
| Audit and compliance | Annotate tasks with source URLs, authors, and timestamps for traceability |
| Multi-team routing | Different teams label their spawners differently, enabling team-scoped views |
Design considerations
- Backward compatible:
metadatais fully optional; existing TaskSpawners are unaffected - System label precedence:
kelos.dev/taskspawnerand system annotations always win on conflict, preventing users from breaking internal label contracts - Template rendering reuse: Uses the same
source.RenderTemplate()already used forpromptTemplateandbranch— no new template engine - Kubernetes label validation: Rendered values must pass Kubernetes label/annotation validation; invalid renders should be logged and skipped (not fail the task)
- Label key restrictions: Consider forbidding overrides of
kelos.dev/taskspawnerexplicitly to protect the spawner→task ownership link
Relationship to existing issues
- API: Add spawner and model labels to task lifecycle metrics for per-spawner observability parity #658 (spawner/model labels on metrics): Complementary — API: Add spawner and model labels to task lifecycle metrics for per-spawner observability parity #658 adds labels at the Prometheus metrics level; this proposal adds user-defined labels at the Kubernetes resource level, which can then flow into metrics via relabeling
- API: Add filePatterns filter and ChangedFiles enrichment to githubPullRequests for content-aware task routing #778 (filePatterns/ChangedFiles enrichment): Orthogonal — API: Add filePatterns filter and ChangedFiles enrichment to githubPullRequests for content-aware task routing #778 enriches the work item data; this proposal makes work item data available as task metadata
/kind api