-
Notifications
You must be signed in to change notification settings - Fork 12
Description
π€ Kelos Strategist Agent @gjkim42
Area: New CRDs & API Extensions
Summary
Kelos agents today are stateless β every task starts from zero with no knowledge of what previous agents did for the same spawner. A cron agent running weekly has no idea what it fixed last week. An issue-triaging agent doesn't know which approaches failed for similar issues. A PR reviewer can't reference patterns it flagged before.
This proposal adds a historyContext field to TaskTemplate that lets the spawner query completed Task outcomes and inject a summarized history into the rendered prompt. This turns stateless, one-shot agents into agents that accumulate institutional knowledge across runs β without requiring external databases, memory stores, or the MCP server proposed in #578.
Problem
1. Cron agents repeat work and miss patterns
The self-development cron spawners (kelos-self-update, kelos-fake-strategist, kelos-fake-user) run on a schedule, but each execution has zero awareness of prior runs. The kelos-fake-strategist prompt includes guidance like "do not create duplicate issues β check existing issues first" (self-development/kelos-fake-strategist.yaml), forcing the agent to re-discover state by calling gh issue list every time. This wastes tokens and is unreliable β the agent must infer from titles whether it already proposed something similar.
If the agent had access to its own prior Results (e.g., "issue_created: #743", "topic: codebase health"), it could deterministically skip areas it already covered.
2. Retry-after-failure lacks error context
When a TaskSpawner retriggers a task for the same work item (via TTL cleanup + rediscovery), the new agent gets the same prompt as the failed one. Issue #730 proposes retryStrategy with "error-context enrichment," but that only applies within a single retry chain. If a task fails, gets cleaned up by TTL, and is rediscovered next poll cycle, the enrichment is lost.
With historyContext, the new task's prompt would automatically include: "A previous agent attempted this and failed with: exit code 1, message: 'make test failed β TestFoo assertion error'" β giving the agent crucial debugging context.
3. The Deps mechanism doesn't help for same-spawner history
The existing {{.Deps.<name>.Results}} template variable (implemented in task_controller.go:858-872) only works for explicit task dependencies within a pipeline. It cannot reference historical tasks from the same spawner, because:
- Previous tasks may have been TTL-deleted
- There's no
dependsOnrelationship between successive spawner runs - The spawner (not the controller) renders the prompt template, and it has no history awareness
4. Industry trend: agent memory is table-stakes
Claude Code has persistent memory files (CLAUDE.md, auto-memory in ~/.claude/). Cursor has .cursorrules. The expectation in the AI agent space is that agents maintain context across sessions. Kelos agents lose all context when a task completes. historyContext is the Kubernetes-native equivalent of agent memory β declarative, auditable, and scoped per spawner.
Proposed API Change
Add a historyContext field to TaskTemplate in api/v1alpha1/taskspawner_types.go:
// TaskTemplate defines the template for spawned Tasks.
type TaskTemplate struct {
// ... existing fields ...
// HistoryContext configures injection of prior task outcomes into prompts.
// When set, the spawner queries recent completed Tasks created by this
// TaskSpawner and makes their Results and Outputs available as the
// {{.History}} template variable in promptTemplate.
// +optional
HistoryContext *HistoryContext `json:"historyContext,omitempty"`
}
// HistoryContext configures how prior task outcomes are collected and
// injected into the prompt template for new tasks.
type HistoryContext struct {
// Limit is the maximum number of recent terminal tasks to include.
// Tasks are ordered by completionTime descending (most recent first).
// Defaults to 5.
// +optional
// +kubebuilder:default=5
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=20
Limit int32 `json:"limit,omitempty"`
// Phases filters which terminal phases to include.
// Valid values: "Succeeded", "Failed". Defaults to both.
// +optional
// +kubebuilder:validation:Items:Enum=Succeeded;Failed
Phases []string `json:"phases,omitempty"`
// IncludeKeys filters which Result keys to include. When empty,
// all Result keys are included. Use this to limit context size
// by selecting only relevant outputs (e.g., ["pr_url", "error", "summary"]).
// +optional
IncludeKeys []string `json:"includeKeys,omitempty"`
}New Template Variable: {{.History}}
When historyContext is configured, the spawner renders a {{.History}} variable containing a structured text summary of prior task outcomes:
=== Task kelos-fake-strategist-743 (Succeeded, 2026-03-18T09:00:00Z) ===
issue_created: #743
topic: codebase health monitoring
duration: 4m32s
=== Task kelos-fake-strategist-722 (Succeeded, 2026-03-11T09:00:00Z) ===
issue_created: #722
topic: dependency upgrade validation
duration: 3m15s
=== Task kelos-fake-strategist-639 (Failed, 2026-03-04T09:00:00Z) ===
error: exit code 1
message: gh issue create failed β rate limit exceeded
duration: 1m02s
When historyContext is nil or no matching historical tasks exist, {{.History}} renders as an empty string, making it safe to use unconditionally in templates.
Concrete Examples
Example 1: Cron strategist with deduplication context
apiVersion: kelos.dev/v1alpha1
kind: TaskSpawner
metadata:
name: weekly-strategist
spec:
when:
cron:
schedule: "0 9 * * 1" # Every Monday at 9am
taskTemplate:
type: claude-code
credentials:
type: oauth
secretRef:
name: claude-credentials
workspaceRef:
name: my-repo
historyContext:
limit: 10
phases: [Succeeded]
includeKeys: [issue_created, topic, summary]
promptTemplate: |
You are a strategic analyst for this codebase.
Identify ONE new improvement opportunity and create a GitHub issue.
## What you've already proposed (do NOT duplicate):
{{.History}}
Focus on areas not covered by prior proposals.Example 2: Worker agent with failure context
apiVersion: kelos.dev/v1alpha1
kind: TaskSpawner
metadata:
name: issue-workers
spec:
when:
githubIssues:
labels: [agent-ready]
taskTemplate:
type: claude-code
credentials:
type: oauth
secretRef:
name: claude-credentials
workspaceRef:
name: my-repo
ttlSecondsAfterFinished: 3600
historyContext:
limit: 3
phases: [Failed]
includeKeys: [error, failed_approach, work_item_id]
promptTemplate: |
{{.Kind}} #{{.Number}}: {{.Title}}
{{.Body}}
{{- if .History}}
## Previous failed attempts on related work:
{{.History}}
Learn from these failures β try a different approach.
{{- end}}Example 3: PR reviewer with pattern memory
apiVersion: kelos.dev/v1alpha1
kind: TaskSpawner
metadata:
name: pr-reviewer
spec:
when:
githubPullRequests:
labels: [needs-review]
taskTemplate:
type: claude-code
credentials:
type: oauth
secretRef:
name: claude-credentials
workspaceRef:
name: my-repo
historyContext:
limit: 5
phases: [Succeeded]
includeKeys: [common_issues, review_summary]
promptTemplate: |
Review PR #{{.Number}}: {{.Title}}
{{.Body}}
{{- if .History}}
## Patterns from recent reviews (for consistency):
{{.History}}
{{- end}}Implementation Approach
Spawner changes (cmd/kelos-spawner/main.go)
The spawner already lists Tasks to count active tasks and check for existing tasks (main.go:250-290). The history context feature would add a query for completed tasks:
// After the existing task list query, if historyContext is configured:
if ts.Spec.TaskTemplate.HistoryContext != nil {
hc := ts.Spec.TaskTemplate.HistoryContext
// List terminal tasks owned by this spawner, sorted by completionTime desc
var allTasks kelosv1alpha1.TaskList
if err := kelosClient.List(ctx, &allTasks,
client.InNamespace(ts.Namespace),
client.MatchingLabels{"kelos.dev/spawner": ts.Name},
); err != nil {
log.Error(err, "listing historical tasks")
}
// Filter by phase, sort by completionTime, take limit
history := buildHistoryContext(allTasks.Items, hc)
// Pass to RenderPrompt as additional template variable
}New function: buildHistoryContext
func buildHistoryContext(tasks []kelosv1alpha1.Task, hc *kelosv1alpha1.HistoryContext) string {
// Filter to terminal phases matching hc.Phases
// Sort by CompletionTime descending
// Take first hc.Limit items
// For each: format Results (filtered by IncludeKeys) as text block
// Return formatted string
}Prompt template changes (internal/source/prompt.go)
Extend WorkItem (or the template context) with a History string field:
type WorkItem struct {
// ... existing fields ...
History string // Rendered historical context, empty if not configured
}The RenderPrompt function already accepts a WorkItem and renders it as a Go template. Adding History as a field requires no changes to the template engine β {{.History}} just works.
Label for spawner ownership
The spawner already creates tasks with ownership metadata. The kelos.dev/spawner label (or equivalent) enables efficient listing. Verify this label exists; if not, add it as part of this change (relates to #649 which proposes metadata propagation).
Handling TTL-deleted tasks
When tasks are cleaned up by ttlSecondsAfterFinished, their Results/Outputs are lost. This is the key design tension:
Option A: Query only living Tasks (simplest) β Works if TTL is long enough to accumulate history. For cron spawners running weekly, a TTL of 30 days would retain ~4 prior tasks.
Option B: Use TaskRecord (#771) when available β If #771 is implemented, the spawner queries TaskRecords instead of Tasks. TaskRecords survive TTL cleanup and are designed for exactly this purpose.
Option C: Spawner-local cache β The spawner Deployment persists across polls. A small in-memory or ConfigMap-backed cache could retain Results from deleted Tasks. This adds complexity but ensures history survives TTL.
Recommendation: Start with Option A, document the TTL interaction, and design for Option B when #771 lands. Option A works well for cron spawners (where TTL is typically long) and for issue/PR spawners (where tasks are often retained until the issue/PR is closed).
Files to change
| File | Change |
|---|---|
api/v1alpha1/taskspawner_types.go |
Add HistoryContext struct and field to TaskTemplate |
internal/source/work_item.go |
Add History string field to WorkItem |
cmd/kelos-spawner/main.go |
Query historical tasks and populate WorkItem.History |
cmd/kelos-spawner/history.go |
New file: buildHistoryContext function |
cmd/kelos-spawner/history_test.go |
Unit tests for history formatting |
internal/source/prompt_test.go |
Test {{.History}} variable in templates |
Estimated: ~40 lines of types + ~80 lines of history builder + tests.
How This Differs from Existing Proposals
| Proposal | What it does | Why this is different |
|---|---|---|
| #730 (retryStrategy) | Enriches retried tasks with error context | Only works within explicit retry chains; lost on TTL cleanup + rediscovery |
| #771 (TaskRecord) | Persists task snapshots after TTL cleanup | Storage primitive, not a prompt injection mechanism β complementary, not overlapping |
| #578 (MCP Server) | Lets running agents query Kelos API | Runtime agent-to-API bridge; requires MCP infrastructure. historyContext is declarative and zero-config |
| #513 (retrospective) | Cron agent that analyzes PR outcomes | A specific workflow; historyContext is a generic primitive that enables any workflow to be history-aware |
| #283 (taskCompletion) | Chains tasks via completion triggers | Cross-spawner event sourcing; historyContext is intra-spawner prompt enrichment |
{{.Deps}} |
Accesses outputs of dependency tasks | Only works for explicit dependsOn relationships, not for historical tasks |
What This Unlocks
- Self-improving cron agents: Weekly code health agents that track what they've already fixed and focus on new areas
- Smarter retries: Failed tasks whose successors know what went wrong
- Consistent reviews: PR reviewers that maintain a mental model of recurring issues
- Deduplication by design: Strategist agents that avoid proposing the same ideas
- Cost efficiency: Agents that skip redundant investigation by referencing prior findings
- Foundation for API: Add TaskRecord CRD for persistent task completion snapshots that survive TTL cleanupΒ #771: When TaskRecord lands,
historyContextautomatically benefits from persistent storage
Scope and Backward Compatibility
- Purely additive: New optional field, no changes to existing behavior
- Zero config for existing users:
historyContext: null(default) means no change β{{.History}}renders empty - Safe template fallback: If
{{.History}}is used buthistoryContextis nil, the template renders an empty string (standard Go template behavior for zero-value strings) - No new CRDs: Extends existing TaskTemplate within TaskSpawner