diff --git a/internal/examples/self_development_test.go b/internal/examples/self_development_test.go
index b9a13f6e..3a401e2a 100644
--- a/internal/examples/self_development_test.go
+++ b/internal/examples/self_development_test.go
@@ -18,6 +18,7 @@ import (
sigyaml "sigs.k8s.io/yaml"
kelos "github.com/kelos-dev/kelos/api/v1alpha2"
+ "github.com/kelos-dev/kelos/internal/sessionbuilder"
"github.com/kelos-dev/kelos/internal/webhook"
)
@@ -63,7 +64,26 @@ func TestSelfDevelopmentGitHubSpawnersUseWebhooks(t *testing.T) {
}
}
-func TestSelfDevelopmentAgentConfigsUseSharedSkills(t *testing.T) {
+func TestSelfDevelopmentBaseAgentProvidesSharedInstructionsAndSkills(t *testing.T) {
+ t.Parallel()
+
+ config := readAgentConfigFromDir(t, "self-development", "base-agent.yaml")
+ wantSkills := []kelos.SkillsShSpec{{Source: "gjkim42/kanon-repo"}}
+ if !reflect.DeepEqual(config.Spec.Skills, wantSkills) {
+ t.Fatalf("base-agent skills = %v, want %v", config.Spec.Skills, wantSkills)
+ }
+ for _, instruction := range []string{
+ "Use plain English in code comments and docstrings",
+ "Use `gh` CLI for GitHub operations",
+ "Use dependency injection for components",
+ } {
+ if !strings.Contains(config.Spec.AgentsMD, instruction) {
+ t.Fatalf("base-agent agentsMD does not contain %q", instruction)
+ }
+ }
+}
+
+func TestSelfDevelopmentRoleAgentConfigsDoNotDuplicateBaseSkills(t *testing.T) {
t.Parallel()
files := []struct {
@@ -100,9 +120,70 @@ func TestSelfDevelopmentAgentConfigsUseSharedSkills(t *testing.T) {
t.Parallel()
config := readAgentConfigFromDir(t, tt.dir, tt.file)
- want := []kelos.SkillsShSpec{{Source: "gjkim42/kanon-repo"}}
- if !reflect.DeepEqual(config.Spec.Skills, want) {
- t.Fatalf("expected %s/%s skills %v, got %v", tt.dir, tt.file, want, config.Spec.Skills)
+ if len(config.Spec.Skills) != 0 {
+ t.Fatalf("%s/%s duplicates base-agent skills: %v", tt.dir, tt.file, config.Spec.Skills)
+ }
+ })
+ }
+}
+
+func TestSelfDevelopmentSpawnersUseBaseAgent(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ dir string
+ file string
+ refs []kelos.AgentConfigReference
+ }{
+ {dir: "self-development", file: "kelos-api-reviewer.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-api-reviewer-agent"}}},
+ {dir: "self-development", file: "kelos-config-update.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-dev-agent"}}},
+ {dir: "self-development", file: "kelos-fake-strategist.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-fake-strategist-agent"}}},
+ {dir: "self-development", file: "kelos-fake-user.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-fake-user-agent"}}},
+ {dir: "self-development", file: "kelos-glm-api-reviewer.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-glm-api-reviewer-agent"}}},
+ {dir: "self-development", file: "kelos-glm-reviewer.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-glm-reviewer-agent"}}},
+ {dir: "self-development", file: "kelos-image-update.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-image-update-agent"}}},
+ {dir: "self-development", file: "kelos-planner.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-planner-agent"}}},
+ {dir: "self-development", file: "kelos-pr-responder.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-dev-agent"}}},
+ {dir: "self-development", file: "kelos-reviewer.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-reviewer-agent"}}},
+ {dir: "self-development", file: "kelos-self-update.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-self-update-agent"}}},
+ {dir: "self-development", file: "kelos-squash-commits.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-dev-agent"}}},
+ {dir: "self-development", file: "kelos-triage.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-dev-agent"}}},
+ {dir: "self-development", file: "kelos-workers.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}}},
+ {dir: "self-development/agora", file: "agora-config-update.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-dev-agent"}}},
+ {dir: "self-development/agora", file: "agora-fake-strategist.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "agora-fake-strategist-agent"}}},
+ {dir: "self-development/agora", file: "agora-fake-user.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "agora-fake-user-agent"}}},
+ {dir: "self-development/agora", file: "agora-planner.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "agora-planner-agent"}}},
+ {dir: "self-development/agora", file: "agora-pr-responder.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "agora-dev-agent"}}},
+ {dir: "self-development/agora", file: "agora-reviewer.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "agora-reviewer-agent"}}},
+ {dir: "self-development/agora", file: "agora-self-update.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-dev-agent"}}},
+ {dir: "self-development/agora", file: "agora-squash-commits.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "agora-dev-agent"}}},
+ {dir: "self-development/agora", file: "agora-triage.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "agora-dev-agent"}}},
+ {dir: "self-development/agora", file: "agora-workers.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "agora-workers-agent"}}},
+ {dir: "self-development/kanon", file: "kanon-config-update.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-dev-agent"}}},
+ {dir: "self-development/kanon", file: "kanon-fake-strategist.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kanon-fake-strategist-agent"}}},
+ {dir: "self-development/kanon", file: "kanon-fake-user.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kanon-fake-user-agent"}}},
+ {dir: "self-development/kanon", file: "kanon-planner.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kanon-planner-agent"}}},
+ {dir: "self-development/kanon", file: "kanon-pr-responder.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kanon-dev-agent"}}},
+ {dir: "self-development/kanon", file: "kanon-reviewer.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kanon-reviewer-agent"}}},
+ {dir: "self-development/kanon", file: "kanon-self-update.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-dev-agent"}}},
+ {dir: "self-development/kanon", file: "kanon-squash-commits.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kanon-dev-agent"}}},
+ {dir: "self-development/kanon", file: "kanon-triage.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kanon-dev-agent"}}},
+ {dir: "self-development/kanon", file: "kanon-workers.yaml", refs: []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kanon-workers-agent"}}},
+ }
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.dir+"/"+tt.file, func(t *testing.T) {
+ t.Parallel()
+
+ taskSpawner, sessionSpawner := readSpawnerFromDir(t, tt.dir, tt.file)
+ var got []kelos.AgentConfigReference
+ if taskSpawner != nil {
+ got = taskSpawner.Spec.TaskTemplate.Worker.AgentConfigRefs
+ } else {
+ got = sessionSpawner.Spec.SessionTemplate.Worker.AgentConfigRefs
+ }
+ if !reflect.DeepEqual(got, tt.refs) {
+ t.Fatalf("%s/%s agentConfigRefs = %v, want %v", tt.dir, tt.file, got, tt.refs)
}
})
}
@@ -164,6 +245,105 @@ func TestDevelopmentSessionSpawnerUsesPersistentWorkspace(t *testing.T) {
if spawner.Spec.SessionTemplate.InitialPrompt == "" {
t.Fatal("SessionSpawner sessionTemplate.initialPrompt is empty")
}
+ if !strings.Contains(spawner.Spec.SessionTemplate.InitialPrompt, "Your workspace is backed by a PVC") {
+ t.Fatal("SessionSpawner initialPrompt does not describe its persistent workspace")
+ }
+ wantAgentConfigs := []kelos.AgentConfigReference{{Name: "base-agent"}}
+ if !reflect.DeepEqual(spawner.Spec.SessionTemplate.Worker.AgentConfigRefs, wantAgentConfigs) {
+ t.Fatalf("SessionSpawner agentConfigRefs = %v, want %v", spawner.Spec.SessionTemplate.Worker.AgentConfigRefs, wantAgentConfigs)
+ }
+}
+
+func TestDevelopmentSessionSpawnerMatchesEveryIssueAndPullRequestComment(t *testing.T) {
+ t.Parallel()
+
+ _, sessionSpawner := readSpawnerFromDir(t, "self-development", "kelos-workers.yaml")
+ if sessionSpawner == nil {
+ t.Fatal("SessionSpawner is nil")
+ }
+ spawner := sessionSpawner.Spec.When.GitHubWebhook
+ if spawner == nil {
+ t.Fatal("SessionSpawner spec.when.githubWebhook is nil")
+ }
+ if !reflect.DeepEqual(spawner.ExcludeAuthors, []string{"kelos-bot[bot]"}) {
+ t.Fatalf("excludeAuthors = %v, want [kelos-bot[bot]]", spawner.ExcludeAuthors)
+ }
+ if len(spawner.Filters) != 1 {
+ t.Fatalf("filters length = %d, want 1", len(spawner.Filters))
+ }
+ filter := spawner.Filters[0]
+ wantFilter := kelos.GitHubWebhookFilter{Event: "issue_comment", Action: "created"}
+ if !reflect.DeepEqual(filter, wantFilter) {
+ t.Fatalf("filter = %#v, want %#v", filter, wantFilter)
+ }
+
+ for _, commentOn := range []string{kelos.CommentOnIssue, kelos.CommentOnPullRequest} {
+ commentOn := commentOn
+ for _, body := range []string{"Please investigate this", "/kelos pick-up"} {
+ body := body
+ t.Run(commentOn+"/"+body, func(t *testing.T) {
+ payloadFilter := filter
+ payloadFilter.CommentOn = commentOn
+ payload := developmentWebhookPayload(t, spawner.Repository, payloadFilter, body)
+ eventData, err := webhook.ParseGitHubWebhook("issue_comment", payload)
+ if err != nil {
+ t.Fatalf("ParseGitHubWebhook() error = %v", err)
+ }
+
+ got, err := webhook.MatchesGitHubEvent(spawner, "issue_comment", eventData)
+ if err != nil {
+ t.Fatalf("MatchesGitHubEvent() error = %v", err)
+ }
+ if !got {
+ t.Fatal("MatchesGitHubEvent() = false, want true")
+ }
+ })
+ }
+ }
+
+ botFilter := filter
+ botFilter.Author = "kelos-bot[bot]"
+ botPayload := developmentWebhookPayload(t, spawner.Repository, botFilter, "/kelos pick-up")
+ botEvent, err := webhook.ParseGitHubWebhook("issue_comment", botPayload)
+ if err != nil {
+ t.Fatalf("ParseGitHubWebhook(bot) error = %v", err)
+ }
+ if got, err := webhook.MatchesGitHubEvent(spawner, "issue_comment", botEvent); err != nil || got {
+ t.Fatalf("MatchesGitHubEvent(bot) = %v, %v, want false, nil", got, err)
+ }
+}
+
+func TestDevelopmentSessionSpawnerUsesPRBranchAndIssueFallback(t *testing.T) {
+ t.Parallel()
+
+ _, spawner := readSpawnerFromDir(t, "self-development", "kelos-workers.yaml")
+ if spawner == nil {
+ t.Fatal("SessionSpawner is nil")
+ }
+
+ tests := []struct {
+ name string
+ branch string
+ want string
+ }{
+ {name: "pull request", branch: "feature-branch", want: "feature-branch"},
+ {name: "issue", branch: "", want: "kelos-task-42"},
+ }
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := sessionbuilder.Render("initialBranch", spawner.Spec.SessionTemplate.InitialBranch, map[string]interface{}{
+ "Branch": tt.branch,
+ "Number": 42,
+ })
+ if err != nil {
+ t.Fatalf("Render() error = %v", err)
+ }
+ if got != tt.want {
+ t.Fatalf("initialBranch = %q, want %q", got, tt.want)
+ }
+ })
+ }
}
func TestDevelopmentCommandPatternsMatchCommandLines(t *testing.T) {
@@ -174,7 +354,6 @@ func TestDevelopmentCommandPatternsMatchCommandLines(t *testing.T) {
file string
command string
}{
- {dir: "self-development", file: "kelos-workers.yaml", command: "/kelos pick-up"},
{dir: "self-development", file: "kelos-planner.yaml", command: "/kelos plan"},
{dir: "self-development", file: "kelos-reviewer.yaml", command: "/kelos review"},
{dir: "self-development", file: "kelos-api-reviewer.yaml", command: "/kelos api-review"},
@@ -561,6 +740,43 @@ func TestManualFakeStrategistTaskStickyIssueCommands(t *testing.T) {
assertStickyIssueCommandsUseTriageAccepted(t, path, string(data))
assertStickyIssueLookupUsesExactMarker(t, path, string(data), "kelos-fake-strategist")
+
+ var task kelos.Task
+ if err := sigyaml.Unmarshal(data, &task); err != nil {
+ t.Fatalf("decoding %s: %v", path, err)
+ }
+ wantAgentConfigs := []kelos.AgentConfigReference{{Name: "base-agent"}, {Name: "kelos-fake-strategist-agent"}}
+ if !reflect.DeepEqual(task.Spec.Worker.AgentConfigRefs, wantAgentConfigs) {
+ t.Fatalf("manual fake strategist agentConfigRefs = %v, want %v", task.Spec.Worker.AgentConfigRefs, wantAgentConfigs)
+ }
+}
+
+func TestInteractiveDevelopmentSessionUsesBaseAgent(t *testing.T) {
+ t.Parallel()
+
+ path := filepath.Join("..", "..", "self-development", "cs")
+ data, err := os.ReadFile(path)
+ if err != nil {
+ t.Fatalf("reading %s: %v", path, err)
+ }
+
+ start := bytes.Index(data, []byte("apiVersion:"))
+ if start < 0 {
+ t.Fatalf("finding embedded Session manifest in %s", path)
+ }
+ end := bytes.Index(data[start:], []byte("\nEOF"))
+ if end < 0 {
+ t.Fatalf("finding embedded Session manifest in %s", path)
+ }
+
+ var session kelos.Session
+ if err := sigyaml.Unmarshal(data[start:start+end], &session); err != nil {
+ t.Fatalf("decoding embedded Session from %s: %v", path, err)
+ }
+ wantAgentConfigs := []kelos.AgentConfigReference{{Name: "base-agent"}}
+ if !reflect.DeepEqual(session.Spec.Worker.AgentConfigRefs, wantAgentConfigs) {
+ t.Fatalf("interactive Session agentConfigRefs = %v, want %v", session.Spec.Worker.AgentConfigRefs, wantAgentConfigs)
+ }
}
func TestAgoraPlannerOnlyTriggersForIssues(t *testing.T) {
@@ -978,6 +1194,9 @@ func assertTaskUsesWorkerSpec(t *testing.T, path string, spec kelos.TaskSpec) {
func assertWorkerSpecComplete(t *testing.T, path string, worker kelos.WorkerSpec) {
t.Helper()
+ if len(worker.AgentConfigRefs) == 0 || worker.AgentConfigRefs[0].Name != "base-agent" {
+ t.Fatalf("%s worker.agentConfigRefs must start with base-agent, got %v", path, worker.AgentConfigRefs)
+ }
if worker.Type == "" {
t.Fatalf("%s worker.type is empty", path)
}
diff --git a/self-development/README.md b/self-development/README.md
index ab4b6512..3bea9891 100644
--- a/self-development/README.md
+++ b/self-development/README.md
@@ -17,12 +17,23 @@ the `kelos-workers` SessionSpawner.
-Each spawner references an `AgentConfig` that defines git identity, comment signatures, and standard constraints. Some agents (triage, pr-responder, squash-commits, config-update) share the base `agentconfig.yaml` (`kelos-dev-agent`), while others (workers, planner, fake-user, fake-strategist, self-update, image-update) define their own `AgentConfig` inline.
+Every self-development Task, TaskSpawner, Session, and SessionSpawner in this
+directory and its nested Agora and Kanon directories references
+[`base-agent.yaml`](base-agent.yaml), which copies
+[`gjkim42/kanon-repo`'s `instructions/AGENTS.md`](https://github.com/gjkim42/kanon-repo/blob/main/instructions/AGENTS.md)
+and installs all skills from that repository through `spec.skills`.
+Tasks and TaskSpawners add a second role-specific AgentConfig when they need
+local identity, conventions, or workflow instructions. The `kelos-workers`
+SessionSpawner and Sessions created with `cs` use only `base-agent`.
-Each self-development `AgentConfig`, including the nested Agora and Kanon
-configurations, also installs all skills from
-[`gjkim42/kanon-repo`](https://github.com/gjkim42/kanon-repo) through
-`spec.skills`, giving every spawner-created agent the same shared skill set.
+Apply the shared AgentConfig before deploying any self-development resource:
+
+```bash
+kubectl apply -f self-development/base-agent.yaml
+```
+
+All other AgentConfigs provide only role- or repository-specific instructions;
+they do not duplicate the shared skills.
Autonomous discovery agents that publish GitHub issues maintain at most one
open `generated-by-kelos` issue slot per TaskSpawner. The issue body includes a
@@ -37,7 +48,7 @@ while a worker or PR responder is handling an explicitly requested issue or PR.
| Spawner | Trigger | Agent | Description |
|---|---|---|---|
-| **kelos-workers** | Webhook: issue comment `/kelos pick-up` | Codex | Picks up issues, creates or updates PRs, self-reviews, and ensures CI passes |
+| **kelos-workers** | Webhook: every new issue/PR conversation comment | Codex | Responds in a durable Session, creates or updates PRs when needed, self-reviews, and ensures CI passes |
| **kelos-planner** | Webhook: issue comment `/kelos plan` | Codex | Investigates an issue and posts a structured implementation plan — advisory only, no code changes |
| **kelos-reviewer** | Webhook: PR comment `/kelos review` | Codex | Reviews PRs on demand — analyzes code, checks conventions, and updates a sticky review comment |
| **kelos-glm-reviewer** | Webhook: PR comment `/kelos glm-review` | GLM-5.2 | Runs a second code review path with Z.AI GLM-5.2 through OpenCode and updates a sticky review comment |
@@ -54,21 +65,25 @@ while a worker or PR responder is handling an explicitly requested issue or PR.
### kelos-workers.yaml
-Picks up open GitHub issues when a maintainer posts `/kelos pick-up` and creates
-a durable Session for the requested work. Follow-ups can continue through the
-Session's web or terminal clients after the initial turn.
+Creates a durable Session for every new non-bot comment in an issue or pull
+request conversation. The triggering comment is the immediate request;
+`/kelos pick-up` tells the worker to take ownership of the complete issue or PR.
+Follow-ups can continue through the Session's web or terminal clients after the
+initial turn.
| | |
|---|---|
-| **Trigger** | GitHub `issue_comment` webhook with `/kelos pick-up` |
+| **Trigger** | Every GitHub `issue_comment` webhook with action `created`, on issues and PRs |
| **Agent** | Codex |
| **Storage** | 10 GiB persistent volume per created Session |
**Key features:**
- Automatically checks for existing PRs and updates them incrementally
+- Uses the PR head branch for PR comments and `kelos-task-` for issue comments
- Self-reviews PRs before requesting human review
- Ensures CI passes before completion
-- Requires a `/kelos pick-up` comment to pick up an issue (maintainer approval gate)
+- Treats `/kelos pick-up` as a request to handle the complete issue or PR
+- Excludes comments from `kelos-bot[bot]` to prevent self-trigger loops
- Keeps the Session available for later web or terminal follow-ups
- Hands off PR review feedback to `kelos-pr-responder`
- May create separate follow-up issues for out-of-scope discoveries; those
@@ -77,12 +92,13 @@ Session's web or terminal clients after the initial turn.
**Deploy:**
```bash
kubectl delete taskspawner kelos-workers --ignore-not-found
+kubectl apply -f self-development/base-agent.yaml
kubectl apply -f self-development/kelos-workers.yaml
```
The delete is required when migrating an existing installation because
`TaskSpawner/kelos-workers` and `SessionSpawner/kelos-workers` are distinct
-Kubernetes objects. Leaving both installed would run both pickup flows.
+Kubernetes objects. Leaving both installed would run both worker flows.
### kelos-planner.yaml
@@ -326,7 +342,7 @@ Runs daily to update agent configuration based on patterns found in PR reviews.
| **Concurrency** | 1 |
Reviews recent PRs and their review comments to identify recurring feedback patterns, then updates agent configuration accordingly:
-- **Project-level changes** — updates `AGENTS.md` or `self-development/agentconfig.yaml` for conventions that apply to all agents
+- **Project-level changes** — updates `AGENTS.md` or the applicable role-specific AgentConfig
- **Task-specific changes** — updates TaskSpawner prompts in `self-development/*.yaml` or creates/updates AgentConfig for specific agents
Creates PRs with changes for maintainer review. Skips uncertain or contradictory
@@ -413,14 +429,13 @@ kubectl apply -f self-development/kelos-squash-commits.yaml
## Interactive Development Session
`cs NAME` creates a persistent Codex Session with the same Workspace,
-credentials, model, effort, and Git identity as `kelos-workers`. It does not use
-the worker's resource settings or AgentConfig, allowing namespace resource
-defaults to apply without the instructions for an ephemeral autonomous GitHub
-bot.
+credentials, model, effort, Git identity, and `base-agent` AgentConfig as
+`kelos-workers`. It does not use the worker's resource requests and limits,
+allowing namespace resource defaults to apply.
-The Session requires the `kelos-agent` Workspace and `kelos-credentials` Secret
-described below. Its `10Gi` workspace persists across Pod replacement and is
-deleted with the Session.
+The Session requires the `base-agent` AgentConfig, `kelos-agent` Workspace, and
+`kelos-credentials` Secret described below. Its `10Gi` workspace persists
+across Pod replacement and is deleted with the Session.
Add this directory to your `PATH` and run the script from any directory:
@@ -548,7 +563,9 @@ To adapt these examples for your own repository:
state: open
```
- Webhook filter fields the shipped self-development spawners rely on:
+ Webhook filter fields the shipped self-development spawners rely on. The
+ same `githubWebhook` fields are available under `TaskSpawner.spec.when` and
+ `SessionSpawner.spec.when`:
| Field | Where it lives | Purpose |
|---|---|---|
@@ -556,14 +573,15 @@ To adapt these examples for your own repository:
| `bodyPattern` | `TaskSpawner.spec.when.githubWebhook.filters[]` | Go re2 regex match against the comment/review body — the modern replacement for substring-only matching. |
| `excludeBodyPatterns` | `TaskSpawner.spec.when.githubWebhook.filters[]` | Companion to `bodyPattern`: a list of regexes that, if any match, drop the event. Use to carve out bot-echo replies that would otherwise match `bodyPattern`. |
| `commentOn` | `TaskSpawner.spec.when.githubWebhook.filters[]` | Scopes `issue_comment` events to `Issue` or `PullRequest`. GitHub fires `issue_comment` for both, so set this to keep issue-only spawners off PRs (and vice versa). |
- | `author` | `TaskSpawner.spec.when.githubWebhook.filters[]` | Restrict matches to a single sender's username — the maintainer-approval gate every shipped spawner uses. |
+ | `author` | `TaskSpawner.spec.when.githubWebhook.filters[]` | Restrict matches to a single sender's username. Omit it to accept every sender not listed in top-level `excludeAuthors`, as `kelos-workers` does. |
| `draft` | `TaskSpawner.spec.when.githubWebhook.filters[]` | Match by PR draft status. Set `false` to skip drafts; omit to match both. |
See [docs/reference.md](../docs/reference.md#taskspawner) for the full
`TaskSpawner.spec.when.githubWebhook` field reference.
3. **Customize the prompt:**
- - Edit `spec.taskTemplate.promptTemplate` to match your workflow
+ - Edit `spec.taskTemplate.promptTemplate` for a TaskSpawner or
+ `spec.sessionTemplate.initialPrompt` for a SessionSpawner
- Available template variables (Go `text/template` syntax):
| Variable | Description | GitHub Webhook | Cron |
@@ -577,6 +595,8 @@ To adapt these examples for your own repository:
| `{{.Action}}` | GitHub webhook action | `created`, `labeled`, `submitted`, etc. | Empty |
| `{{.Sender}}` | GitHub username that triggered the webhook | GitHub login | Empty |
| `{{.Branch}}` | Branch name when present in the webhook payload | PR head branch or pushed branch; empty for issue events | Empty |
+ | `{{.CommentBody}}` | Triggering comment or review body | Available for comment and review events | Empty |
+ | `{{.CommentURL}}` | Triggering comment or review URL | Available for comment and review events | Empty |
| `{{.Kind}}` | Type of work item | `"webhook"` | `"Issue"` |
| `{{.Time}}` | Trigger time (RFC3339) | Empty | Cron tick time (e.g., `"2026-02-07T09:00:00Z"`) |
| `{{.Schedule}}` | Cron schedule expression | Empty | Schedule string (e.g., `"0 * * * *"`) |
@@ -613,7 +633,7 @@ The key pattern in these examples is webhook-triggered handoff plus runtime re-v
2. The matching TaskSpawner creates a Task, or `kelos-workers` creates a Session
3. The agent re-reads the latest issue or PR state with `gh` before acting, so asynchronous label updates are respected
4. If the agent needs human input, it posts a plain-English status comment describing what happened
-5. A fresh `/kelos pick-up`, `/kelos plan`, `/kelos review`, `/kelos glm-review`, `/kelos api-review`, `/kelos glm-api-review`, `/kelos squash-commits`, or relabel event retriggers automation later
+5. Every fresh issue or PR conversation comment creates a `kelos-workers` Session; explicit commands or relabel events retrigger the other matching automation
Each matching webhook delivery creates a discrete Task or Session. A created
Session remains available for interactive follow-ups through Session clients.
diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml
index e2b2b5fd..6f93c08c 100644
--- a/self-development/agentconfig.yaml
+++ b/self-development/agentconfig.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kelos-dev-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kelos Development Agent
diff --git a/self-development/agora/README.md b/self-development/agora/README.md
index 2392ac3b..3a1b3c91 100644
--- a/self-development/agora/README.md
+++ b/self-development/agora/README.md
@@ -11,11 +11,12 @@ the Agora repository.
## How It Works
-Each TaskSpawner references an `AgentConfig` that defines git identity, comment
-signatures, and standard constraints. Some agents (pr-responder, triage,
-squash-commits) share the base `agentconfig.yaml` (`agora-dev-agent`), while
-others (workers, planner, reviewer, fake-user, fake-strategist) define their own
-`AgentConfig` inline.
+Each TaskSpawner references the root [`base-agent`](../base-agent.yaml) first
+for shared instructions and skills. It then references an AgentConfig with
+repository- or role-specific instructions: pr-responder, triage, and
+squash-commits share `agentconfig.yaml` (`agora-dev-agent`), while workers,
+planner, reviewer, fake-user, and fake-strategist define their own AgentConfig
+inline.
Autonomous discovery agents that publish GitHub issues maintain at most one
open `generated-by-kelos` issue slot per TaskSpawner. The issue body includes a
@@ -30,7 +31,7 @@ Eight spawners operate directly on the Agora repository through the
`agora-agent` Workspace. The two meta-maintenance spawners
(`agora-config-update`, `agora-self-update`) are different: the files they
maintain (`self-development/agora/*`) live in *this* repository, so they use the
-`kelos-agent` Workspace and the `kelos-dev-agent` AgentConfig from
+`kelos-agent` Workspace and the `kelos-dev-agent` role AgentConfig from
`self-development/`, and they read Agora's activity cross-repo with
`gh ... --repo kelos-dev/agora`.
@@ -53,16 +54,18 @@ maintain (`self-development/agora/*`) live in *this* repository, so they use the
> Kubernetes CRDs to review) and `kelos-image-update` (it updates
> coding-agent image versions, not service base images).
-Apply the whole directory at once — this includes `agentconfig.yaml`, which
-defines the shared `agora-dev-agent` referenced by the pr-responder, triage, and
-squash-commits spawners:
+Apply the root `base-agent` first, then the whole directory. The directory
+includes `agentconfig.yaml`, which defines the `agora-dev-agent` role
+instructions referenced by the pr-responder, triage, and squash-commits
+spawners:
```bash
+kubectl apply -f self-development/base-agent.yaml
kubectl apply -f self-development/agora/
```
The per-spawner `kubectl apply` commands below are for deploying or updating an
-individual spawner.
+individual spawner after `base-agent` is installed.
### agora-workers.yaml
@@ -243,7 +246,7 @@ Runs daily to update the Agora agent configuration based on patterns found in Ag
| **Workspace** | `kelos-agent` (edits `self-development/agora/` in this repo) |
| **Concurrency** | 1 |
-Reviews recent `kelos-dev/agora` PRs and their review comments to identify recurring feedback patterns, then updates the configuration under `self-development/agora/` (the shared `agentconfig.yaml` or a specific TaskSpawner prompt). Opens a PR against this repository using `/kind cleanup` and `release-note: NONE`, since it only touches `self-development/agora/`.
+Reviews recent `kelos-dev/agora` PRs and their review comments to identify recurring feedback patterns, then updates the configuration under `self-development/agora/` (the shared Agora role instructions in `agentconfig.yaml` or a specific TaskSpawner prompt). Opens a PR against this repository using `/kind cleanup` and `release-note: NONE`, since it only touches `self-development/agora/`.
Skips uncertain or contradictory feedback, and skips an existing configuration
PR when it has assignees.
diff --git a/self-development/agora/agentconfig.yaml b/self-development/agora/agentconfig.yaml
index 49adb30a..460a2e7e 100644
--- a/self-development/agora/agentconfig.yaml
+++ b/self-development/agora/agentconfig.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: agora-dev-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Agora Development Agent
diff --git a/self-development/agora/agora-config-update.yaml b/self-development/agora/agora-config-update.yaml
index 0f05e344..b5d1b99b 100644
--- a/self-development/agora/agora-config-update.yaml
+++ b/self-development/agora/agora-config-update.yaml
@@ -39,6 +39,7 @@ spec:
- name: GIT_COMMITTER_EMAIL
value: "gjkim042@gmail.com"
agentConfigRefs:
+ - name: base-agent
- name: kelos-dev-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
@@ -60,7 +61,8 @@ spec:
The Agora agent configuration lives in this repository (github.com/kelos-dev/kelos),
under `self-development/agora/`:
- - `self-development/agora/agentconfig.yaml` — shared AgentConfig (`agora-dev-agent`)
+ - `self-development/base-agent.yaml` — shared instructions and skills used by every resource
+ - `self-development/agora/agentconfig.yaml` — shared Agora role instructions (`agora-dev-agent`)
- `self-development/agora/*.yaml` — TaskSpawner prompt templates and per-agent AgentConfigs
You are checked out on the kelos repository. Read Agora's activity with
@@ -90,6 +92,7 @@ spec:
**Shared changes** (affect all Agora agents):
- Update `self-development/agora/agentconfig.yaml` if the feedback is about shared agent behavior or project conventions
+ - Keep shared skills only in `self-development/base-agent.yaml`; do not duplicate them in Agora role AgentConfigs
**Task-specific changes** (affect a specific agent):
- Update the relevant TaskSpawner prompt in `self-development/agora/*.yaml`
diff --git a/self-development/agora/agora-fake-strategist.yaml b/self-development/agora/agora-fake-strategist.yaml
index ee1bf0f1..97a5b413 100644
--- a/self-development/agora/agora-fake-strategist.yaml
+++ b/self-development/agora/agora-fake-strategist.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: agora-fake-strategist-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Agora Strategist Agent
@@ -59,6 +57,7 @@ spec:
limits:
ephemeral-storage: "2Gi"
agentConfigRefs:
+ - name: base-agent
- name: agora-fake-strategist-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/agora/agora-fake-user.yaml b/self-development/agora/agora-fake-user.yaml
index 87001df9..ce766eef 100644
--- a/self-development/agora/agora-fake-user.yaml
+++ b/self-development/agora/agora-fake-user.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: agora-fake-user-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Agora User Agent
@@ -59,6 +57,7 @@ spec:
limits:
ephemeral-storage: "2Gi"
agentConfigRefs:
+ - name: base-agent
- name: agora-fake-user-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/agora/agora-planner.yaml b/self-development/agora/agora-planner.yaml
index 712df854..992e9b1f 100644
--- a/self-development/agora/agora-planner.yaml
+++ b/self-development/agora/agora-planner.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: agora-planner-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Agora Planner Agent
@@ -64,6 +62,7 @@ spec:
limits:
ephemeral-storage: "2Gi"
agentConfigRefs:
+ - name: base-agent
- name: agora-planner-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/agora/agora-pr-responder.yaml b/self-development/agora/agora-pr-responder.yaml
index 090ef712..df0dee9b 100644
--- a/self-development/agora/agora-pr-responder.yaml
+++ b/self-development/agora/agora-pr-responder.yaml
@@ -57,6 +57,7 @@ spec:
- name: GIT_COMMITTER_EMAIL
value: "gjkim042@gmail.com"
agentConfigRefs:
+ - name: base-agent
- name: agora-dev-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/agora/agora-reviewer.yaml b/self-development/agora/agora-reviewer.yaml
index a447fd82..bed8369d 100644
--- a/self-development/agora/agora-reviewer.yaml
+++ b/self-development/agora/agora-reviewer.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: agora-reviewer-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Agora Reviewer Agent
@@ -83,6 +81,7 @@ spec:
limits:
ephemeral-storage: "4Gi"
agentConfigRefs:
+ - name: base-agent
- name: agora-reviewer-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/agora/agora-self-update.yaml b/self-development/agora/agora-self-update.yaml
index f2dc6bef..87fbd218 100644
--- a/self-development/agora/agora-self-update.yaml
+++ b/self-development/agora/agora-self-update.yaml
@@ -30,6 +30,7 @@ spec:
limits:
ephemeral-storage: "2Gi"
agentConfigRefs:
+ - name: base-agent
- name: kelos-dev-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
@@ -48,10 +49,11 @@ spec:
define how the Agora agents develop the Agora project (github.com/kelos-dev/agora).
Those workflow files live in this repository (github.com/kelos-dev/kelos),
- under `self-development/agora/`: TaskSpawner definitions and the shared AgentConfig
- that orchestrate Agora's autonomous development loop. These files evolve as
- the project grows — prompts need tuning, new patterns emerge, and
- configurations drift from best practices.
+ under `self-development/agora/`: TaskSpawner definitions and role-specific
+ AgentConfigs that orchestrate Agora's autonomous development loop. Every
+ resource references `self-development/base-agent.yaml` first for shared
+ instructions and skills. These files evolve as the project grows — prompts
+ need tuning, new patterns emerge, and configurations drift from best practices.
You are checked out on the kelos repository. Read Agora's activity with
`gh ... --repo kelos-dev/agora`, and file any improvement issues against
@@ -71,6 +73,7 @@ spec:
- Check that resource requests/limits, models, TTLs, and other settings are appropriate
- Verify that webhook repositories (`kelos-dev/agora`), events, labels, excludeLabels, and filters are correct and consistent
- Ensure AgentConfig instructions in `self-development/agora/agentconfig.yaml` match Agora's current conventions and Makefile targets
+ - Ensure every TaskSpawner references `base-agent` first and role-specific AgentConfigs do not duplicate its skills
- Look for inconsistencies between TaskSpawners that should share the same configuration
3. **Workflow Completeness**
diff --git a/self-development/agora/agora-squash-commits.yaml b/self-development/agora/agora-squash-commits.yaml
index f582d258..0ec993a4 100644
--- a/self-development/agora/agora-squash-commits.yaml
+++ b/self-development/agora/agora-squash-commits.yaml
@@ -54,6 +54,7 @@ spec:
- name: GIT_COMMITTER_EMAIL
value: "gjkim042@gmail.com"
agentConfigRefs:
+ - name: base-agent
- name: agora-dev-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/agora/agora-triage.yaml b/self-development/agora/agora-triage.yaml
index 0cb838bb..d9a5fb75 100644
--- a/self-development/agora/agora-triage.yaml
+++ b/self-development/agora/agora-triage.yaml
@@ -44,6 +44,7 @@ spec:
limits:
ephemeral-storage: "2Gi"
agentConfigRefs:
+ - name: base-agent
- name: agora-dev-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/agora/agora-workers.yaml b/self-development/agora/agora-workers.yaml
index df2c36e0..4069e880 100644
--- a/self-development/agora/agora-workers.yaml
+++ b/self-development/agora/agora-workers.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: agora-workers-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Agora Worker Agent
@@ -85,6 +83,7 @@ spec:
- name: GIT_COMMITTER_EMAIL
value: "gjkim042@gmail.com"
agentConfigRefs:
+ - name: base-agent
- name: agora-workers-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/base-agent.yaml b/self-development/base-agent.yaml
new file mode 100644
index 00000000..eb2bde96
--- /dev/null
+++ b/self-development/base-agent.yaml
@@ -0,0 +1,25 @@
+apiVersion: kelos.dev/v1alpha2
+kind: AgentConfig
+metadata:
+ name: base-agent
+spec:
+ skills:
+ - source: gjkim42/kanon-repo
+ agentsMD: |
+ # AGENTS.md
+
+ ## Rules
+
+ - Use plain English in code comments and docstrings unless otherwise specified.
+ - Write comments only when they add information the code cannot express. Prefer comments that explain why a decision exists, and avoid comments that merely restate what the code does. If code needs a comment to be understandable, simplify or rename it first. Comments are appropriate for complex regular expressions, algorithms, non-obvious constraints, and tradeoffs. When editing code, review nearby existing comments and TODOs, and remove or update any that are obsolete. Use docstrings and API documentation to explain purpose, usage, and behavior.
+ - Never encode change history in code. Comments, docstrings, and identifiers must describe only the current state — no "old vs new", "before/after", "previously/now", "changed from X to Y" comments, and no names like `newParser`, `handleV2`, or `legacyFoo`. Once merged, today's "new" is just the code (and soon the old one), so such references rot immediately. Change history belongs in git commits and PR descriptions.
+ - When a significant design decision changes mid-work (a different approach, a renamed concept, a replaced mechanism), rewrite the affected code, comments, tests, and docs so the result reads as if it had been designed this way from the beginning. Remove every leftover of the abandoned approach: dead branches, shims and indirection that existed only to serve the old design, names and structure shaped by it, tests that pin its behavior, and doc sections that describe it. This cleanup covers only the code touched by the decision — it is not a license for unrelated refactors. The diff may show the journey; the final tree must not.
+ - Do not rewrite existing comments or docstrings only because they are written in Korean. Preserve them unless they are incorrect, obsolete, unclear for the current change, or the user explicitly asks to translate them.
+ - Use `gh` CLI for GitHub operations (PRs, issues, repos, etc.) instead of MCP OSS tools.
+ - Do not prefix PR titles with `[codex]`.
+ - When addressing PR feedback, treat reviews as findings to verify, not instructions to execute blindly. First identify the current PR head, review state, author, timestamp, thread resolution, and whether the comment still applies to the current diff. Fix only feedback that is valid, current, actionable, and supported by the code; explicitly triage stale, resolved, superseded, speculative, preference-only, or non-actionable feedback instead of changing code for it.
+ - Make patches as simple as possible: solve the requested problem directly, keep changes narrowly scoped, follow existing patterns, and avoid opportunistic refactors, dependency churn, or formatting-only edits unless they are necessary for the task. Do not add defensive handling for unreasonable or highly unlikely cases unless there is a concrete requirement, observed failure, or codebase contract that justifies it.
+ - Treat CI-only and test-environment flakes as test or infrastructure problems unless there is concrete evidence of a product/runtime bug. Do not change product behavior just to mask CI timing, ordering, caching, or environment issues; fix the harness, readiness checks, isolation, or diagnostics instead.
+ - Add a regression test only when it pins a failure mode the shipped code must hold against — a defect present on the base branch (diff against `git merge-base HEAD origin/main`, falling back to develop) that this change fixes. Do not add regression tests for bugs introduced and then fixed within the same branch or session; that code never shipped, so the test guards nothing and just locks in transient work-in-progress. If a transient mid-session failure exposes a genuine edge case the final code must handle, cover it as ordinary edge-case test, not a regression test. Always write normal tests for the intended behavior of new or changed code.
+ - Use structured logging (key=value fields) when the project's logger supports it. Log messages should be concise plain English phrases or sentences that describe what happened. Put variable values and machine-readable categories in structured fields instead of interpolating them into the message string.
+ - Use dependency injection for components, especially anything env-derived. Clients / sub-components take their config as required constructor arguments and validate it there (fail fast on empty/invalid). Only the composition root (the application entry point) reads environment variables; every other module receives values via injection. No env-var fallbacks inside constructors, no module-level env-derived defaults inside client packages.
diff --git a/self-development/cs b/self-development/cs
index 4c625517..42af22c9 100755
--- a/self-development/cs
+++ b/self-development/cs
@@ -32,6 +32,8 @@ spec:
requests:
storage: 10Gi
worker:
+ agentConfigRefs:
+ - name: base-agent
workspaceRef:
name: kelos-agent
model: gpt-5.6-sol
diff --git a/self-development/kanon/README.md b/self-development/kanon/README.md
index 1ae0739f..d91fdcad 100644
--- a/self-development/kanon/README.md
+++ b/self-development/kanon/README.md
@@ -12,11 +12,12 @@ the agents they spawn operate on the Kanon repository.
## How It Works
-Each TaskSpawner references an `AgentConfig` that defines git identity, comment
-signatures, and standard constraints. Some agents (pr-responder, triage,
-squash-commits) share the base `agentconfig.yaml` (`kanon-dev-agent`), while
-others (workers, planner, reviewer, fake-user, fake-strategist) define their own
-`AgentConfig` inline.
+Each TaskSpawner references the root [`base-agent`](../base-agent.yaml) first
+for shared instructions and skills. It then references an AgentConfig with
+repository- or role-specific instructions: pr-responder, triage, and
+squash-commits share `agentconfig.yaml` (`kanon-dev-agent`), while workers,
+planner, reviewer, fake-user, and fake-strategist define their own AgentConfig
+inline.
Autonomous discovery agents that publish GitHub issues maintain at most one
open `generated-by-kelos` issue slot per TaskSpawner. The issue body includes a
@@ -31,7 +32,7 @@ Eight spawners operate directly on the Kanon repository through the
`kanon-agent` Workspace. The two meta-maintenance spawners
(`kanon-config-update`, `kanon-self-update`) are different: the files they
maintain (`self-development/kanon/*`) live in *this* repository, so they use the
-`kelos-agent` Workspace and the `kelos-dev-agent` AgentConfig from
+`kelos-agent` Workspace and the `kelos-dev-agent` role AgentConfig from
`self-development/`, and they read Kanon's activity cross-repo with
`gh ... --repo kelos-dev/kanon`.
@@ -54,16 +55,18 @@ maintain (`self-development/kanon/*`) live in *this* repository, so they use the
> Kubernetes CRDs/API surface to review) and `kelos-image-update` (Kanon has no
> coding-agent Dockerfiles to bump).
-Apply the whole directory at once — this includes `agentconfig.yaml`, which
-defines the shared `kanon-dev-agent` referenced by the pr-responder, triage, and
-squash-commits spawners:
+Apply the root `base-agent` first, then the whole directory. The directory
+includes `agentconfig.yaml`, which defines the `kanon-dev-agent` role
+instructions referenced by the pr-responder, triage, and squash-commits
+spawners:
```bash
+kubectl apply -f self-development/base-agent.yaml
kubectl apply -f self-development/kanon/
```
The per-spawner `kubectl apply` commands below are for deploying or updating an
-individual spawner.
+individual spawner after `base-agent` is installed.
### kanon-workers.yaml
@@ -244,7 +247,7 @@ Runs daily to update the Kanon agent configuration based on patterns found in Ka
| **Workspace** | `kelos-agent` (edits `self-development/kanon/` in this repo) |
| **Concurrency** | 1 |
-Reviews recent `kelos-dev/kanon` PRs and their review comments to identify recurring feedback patterns, then updates the configuration under `self-development/kanon/` (the shared `agentconfig.yaml` or a specific TaskSpawner prompt). Opens a PR against this repository using `/kind cleanup` and `release-note: NONE`, since it only touches `self-development/kanon/`.
+Reviews recent `kelos-dev/kanon` PRs and their review comments to identify recurring feedback patterns, then updates the configuration under `self-development/kanon/` (the shared Kanon role instructions in `agentconfig.yaml` or a specific TaskSpawner prompt). Opens a PR against this repository using `/kind cleanup` and `release-note: NONE`, since it only touches `self-development/kanon/`.
Skips uncertain or contradictory feedback, and skips an existing configuration
PR when it has assignees.
diff --git a/self-development/kanon/agentconfig.yaml b/self-development/kanon/agentconfig.yaml
index 2885c787..361afb50 100644
--- a/self-development/kanon/agentconfig.yaml
+++ b/self-development/kanon/agentconfig.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kanon-dev-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kanon Development Agent
diff --git a/self-development/kanon/kanon-config-update.yaml b/self-development/kanon/kanon-config-update.yaml
index 10573526..7b10aa2b 100644
--- a/self-development/kanon/kanon-config-update.yaml
+++ b/self-development/kanon/kanon-config-update.yaml
@@ -39,6 +39,7 @@ spec:
- name: GIT_COMMITTER_EMAIL
value: "gjkim042@gmail.com"
agentConfigRefs:
+ - name: base-agent
- name: kelos-dev-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
@@ -60,7 +61,8 @@ spec:
The Kanon agent configuration lives in this repository (github.com/kelos-dev/kelos),
under `self-development/kanon/`:
- - `self-development/kanon/agentconfig.yaml` — shared AgentConfig (`kanon-dev-agent`)
+ - `self-development/base-agent.yaml` — shared instructions and skills used by every resource
+ - `self-development/kanon/agentconfig.yaml` — shared Kanon role instructions (`kanon-dev-agent`)
- `self-development/kanon/*.yaml` — TaskSpawner prompt templates and per-agent AgentConfigs
You are checked out on the kelos repository. Read Kanon's activity with
@@ -90,6 +92,7 @@ spec:
**Shared changes** (affect all Kanon agents):
- Update `self-development/kanon/agentconfig.yaml` if the feedback is about shared agent behavior or project conventions
+ - Keep shared skills only in `self-development/base-agent.yaml`; do not duplicate them in Kanon role AgentConfigs
**Task-specific changes** (affect a specific agent):
- Update the relevant TaskSpawner prompt in `self-development/kanon/*.yaml`
diff --git a/self-development/kanon/kanon-fake-strategist.yaml b/self-development/kanon/kanon-fake-strategist.yaml
index a5c9cecf..b5e32fb3 100644
--- a/self-development/kanon/kanon-fake-strategist.yaml
+++ b/self-development/kanon/kanon-fake-strategist.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kanon-fake-strategist-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kanon Strategist Agent
@@ -57,6 +55,7 @@ spec:
limits:
ephemeral-storage: "2Gi"
agentConfigRefs:
+ - name: base-agent
- name: kanon-fake-strategist-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kanon/kanon-fake-user.yaml b/self-development/kanon/kanon-fake-user.yaml
index 9b3ef52e..b48bc40f 100644
--- a/self-development/kanon/kanon-fake-user.yaml
+++ b/self-development/kanon/kanon-fake-user.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kanon-fake-user-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kanon User Agent
@@ -57,6 +55,7 @@ spec:
limits:
ephemeral-storage: "2Gi"
agentConfigRefs:
+ - name: base-agent
- name: kanon-fake-user-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kanon/kanon-planner.yaml b/self-development/kanon/kanon-planner.yaml
index a686eded..09eb5dc4 100644
--- a/self-development/kanon/kanon-planner.yaml
+++ b/self-development/kanon/kanon-planner.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kanon-planner-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kanon Planner Agent
@@ -63,6 +61,7 @@ spec:
limits:
ephemeral-storage: "2Gi"
agentConfigRefs:
+ - name: base-agent
- name: kanon-planner-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kanon/kanon-pr-responder.yaml b/self-development/kanon/kanon-pr-responder.yaml
index 4ce0575f..cdc77d89 100644
--- a/self-development/kanon/kanon-pr-responder.yaml
+++ b/self-development/kanon/kanon-pr-responder.yaml
@@ -57,6 +57,7 @@ spec:
- name: GIT_COMMITTER_EMAIL
value: "gjkim042@gmail.com"
agentConfigRefs:
+ - name: base-agent
- name: kanon-dev-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kanon/kanon-reviewer.yaml b/self-development/kanon/kanon-reviewer.yaml
index 006ea4cf..5d103b81 100644
--- a/self-development/kanon/kanon-reviewer.yaml
+++ b/self-development/kanon/kanon-reviewer.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kanon-reviewer-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kanon Reviewer Agent
@@ -83,6 +81,7 @@ spec:
limits:
ephemeral-storage: "4Gi"
agentConfigRefs:
+ - name: base-agent
- name: kanon-reviewer-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kanon/kanon-self-update.yaml b/self-development/kanon/kanon-self-update.yaml
index 36126849..c3a2051c 100644
--- a/self-development/kanon/kanon-self-update.yaml
+++ b/self-development/kanon/kanon-self-update.yaml
@@ -30,6 +30,7 @@ spec:
limits:
ephemeral-storage: "2Gi"
agentConfigRefs:
+ - name: base-agent
- name: kelos-dev-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
@@ -48,10 +49,11 @@ spec:
define how the Kanon agents develop the Kanon project (github.com/kelos-dev/kanon).
Those workflow files live in this repository (github.com/kelos-dev/kelos),
- under `self-development/kanon/`: TaskSpawner definitions and the shared AgentConfig
- that orchestrate Kanon's autonomous development loop. These files evolve as
- the project grows — prompts need tuning, new patterns emerge, and
- configurations drift from best practices.
+ under `self-development/kanon/`: TaskSpawner definitions and role-specific
+ AgentConfigs that orchestrate Kanon's autonomous development loop. Every
+ resource references `self-development/base-agent.yaml` first for shared
+ instructions and skills. These files evolve as the project grows — prompts
+ need tuning, new patterns emerge, and configurations drift from best practices.
You are checked out on the kelos repository. Read Kanon's activity with
`gh ... --repo kelos-dev/kanon`, and file any improvement issues against
@@ -71,6 +73,7 @@ spec:
- Check that resource requests/limits, models, TTLs, and other settings are appropriate
- Verify that webhook repositories (`kelos-dev/kanon`), events, labels, excludeLabels, and filters are correct and consistent
- Ensure AgentConfig instructions in `self-development/kanon/agentconfig.yaml` match Kanon's current conventions and Makefile targets
+ - Ensure every TaskSpawner references `base-agent` first and role-specific AgentConfigs do not duplicate its skills
- Look for inconsistencies between TaskSpawners that should share the same configuration
3. **Workflow Completeness**
diff --git a/self-development/kanon/kanon-squash-commits.yaml b/self-development/kanon/kanon-squash-commits.yaml
index 0cb2083d..2aa04741 100644
--- a/self-development/kanon/kanon-squash-commits.yaml
+++ b/self-development/kanon/kanon-squash-commits.yaml
@@ -54,6 +54,7 @@ spec:
- name: GIT_COMMITTER_EMAIL
value: "gjkim042@gmail.com"
agentConfigRefs:
+ - name: base-agent
- name: kanon-dev-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kanon/kanon-triage.yaml b/self-development/kanon/kanon-triage.yaml
index effc453c..7f363d2b 100644
--- a/self-development/kanon/kanon-triage.yaml
+++ b/self-development/kanon/kanon-triage.yaml
@@ -44,6 +44,7 @@ spec:
limits:
ephemeral-storage: "2Gi"
agentConfigRefs:
+ - name: base-agent
- name: kanon-dev-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kanon/kanon-workers.yaml b/self-development/kanon/kanon-workers.yaml
index 39152f5a..63b761f2 100644
--- a/self-development/kanon/kanon-workers.yaml
+++ b/self-development/kanon/kanon-workers.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kanon-workers-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kanon Worker Agent
@@ -83,6 +81,7 @@ spec:
- name: GIT_COMMITTER_EMAIL
value: "gjkim042@gmail.com"
agentConfigRefs:
+ - name: base-agent
- name: kanon-workers-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kelos-api-reviewer.yaml b/self-development/kelos-api-reviewer.yaml
index f5a6c793..e28e3d28 100644
--- a/self-development/kelos-api-reviewer.yaml
+++ b/self-development/kelos-api-reviewer.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kelos-api-reviewer-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kelos API Reviewer Agent
@@ -85,6 +83,7 @@ spec:
limits:
ephemeral-storage: "4Gi"
agentConfigRefs:
+ - name: base-agent
- name: kelos-api-reviewer-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kelos-config-update.yaml b/self-development/kelos-config-update.yaml
index bc9aa00f..d742d188 100644
--- a/self-development/kelos-config-update.yaml
+++ b/self-development/kelos-config-update.yaml
@@ -36,6 +36,7 @@ spec:
- name: GIT_COMMITTER_EMAIL
value: "gjkim042@gmail.com"
agentConfigRefs:
+ - name: base-agent
- name: kelos-dev-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
@@ -55,7 +56,8 @@ spec:
Agent configuration includes:
- `AGENTS.md` — project-level conventions and instructions for all agents
- - `self-development/agentconfig.yaml` — shared AgentConfig (agentsMD, plugins, skills, subagents, commands)
+ - `self-development/base-agent.yaml` — checked-in copy of `gjkim42/kanon-repo` instructions and shared skills
+ - `self-development/agentconfig.yaml` — Kelos-specific AgentConfig used by general-purpose TaskSpawners
- `self-development/*.yaml` — TaskSpawner and SessionSpawner prompt templates that guide each agent's behavior
Task:
@@ -81,7 +83,8 @@ spec:
**Project-level changes** (affect all agents):
- Update `AGENTS.md` if the feedback is about general project conventions
- - Update `self-development/agentconfig.yaml` if the feedback is about shared agent behavior
+ - Update the applicable role-specific AgentConfig for shared TaskSpawner behavior
+ - Only update `self-development/base-agent.yaml` to synchronize changes from its upstream `gjkim42/kanon-repo` sources
**Task-specific changes** (affect a specific agent):
- Update the relevant spawner prompt in `self-development/*.yaml`
diff --git a/self-development/kelos-fake-strategist.yaml b/self-development/kelos-fake-strategist.yaml
index 90b0842c..2cd98613 100644
--- a/self-development/kelos-fake-strategist.yaml
+++ b/self-development/kelos-fake-strategist.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kelos-fake-strategist-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kelos Strategist Agent
@@ -61,6 +59,7 @@ spec:
limits:
ephemeral-storage: "2Gi"
agentConfigRefs:
+ - name: base-agent
- name: kelos-fake-strategist-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kelos-fake-user.yaml b/self-development/kelos-fake-user.yaml
index 98c34246..f29cb7dc 100644
--- a/self-development/kelos-fake-user.yaml
+++ b/self-development/kelos-fake-user.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kelos-fake-user-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kelos User Agent
@@ -61,6 +59,7 @@ spec:
limits:
ephemeral-storage: "2Gi"
agentConfigRefs:
+ - name: base-agent
- name: kelos-fake-user-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kelos-glm-api-reviewer.yaml b/self-development/kelos-glm-api-reviewer.yaml
index d44f3613..f148f4c3 100644
--- a/self-development/kelos-glm-api-reviewer.yaml
+++ b/self-development/kelos-glm-api-reviewer.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kelos-glm-api-reviewer-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kelos GLM API Reviewer Agent
@@ -88,6 +86,7 @@ spec:
limits:
ephemeral-storage: "4Gi"
agentConfigRefs:
+ - name: base-agent
- name: kelos-glm-api-reviewer-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kelos-glm-reviewer.yaml b/self-development/kelos-glm-reviewer.yaml
index b63d04b5..66cc1231 100644
--- a/self-development/kelos-glm-reviewer.yaml
+++ b/self-development/kelos-glm-reviewer.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kelos-glm-reviewer-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kelos GLM Reviewer Agent
@@ -89,6 +87,7 @@ spec:
limits:
ephemeral-storage: "4Gi"
agentConfigRefs:
+ - name: base-agent
- name: kelos-glm-reviewer-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kelos-image-update.yaml b/self-development/kelos-image-update.yaml
index bc8af27b..fce81692 100644
--- a/self-development/kelos-image-update.yaml
+++ b/self-development/kelos-image-update.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kelos-image-update-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kelos Image Update Agent
@@ -59,6 +57,7 @@ spec:
- name: GIT_COMMITTER_EMAIL
value: "gjkim042@gmail.com"
agentConfigRefs:
+ - name: base-agent
- name: kelos-image-update-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kelos-planner.yaml b/self-development/kelos-planner.yaml
index 1de2589a..3de27a26 100644
--- a/self-development/kelos-planner.yaml
+++ b/self-development/kelos-planner.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kelos-planner-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kelos Planner Agent
@@ -63,6 +61,7 @@ spec:
limits:
ephemeral-storage: "2Gi"
agentConfigRefs:
+ - name: base-agent
- name: kelos-planner-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kelos-pr-responder.yaml b/self-development/kelos-pr-responder.yaml
index 687765b9..9bc9c692 100644
--- a/self-development/kelos-pr-responder.yaml
+++ b/self-development/kelos-pr-responder.yaml
@@ -57,6 +57,7 @@ spec:
- name: GIT_COMMITTER_EMAIL
value: "gjkim042@gmail.com"
agentConfigRefs:
+ - name: base-agent
- name: kelos-dev-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kelos-reviewer.yaml b/self-development/kelos-reviewer.yaml
index bc680821..5f55d0ec 100644
--- a/self-development/kelos-reviewer.yaml
+++ b/self-development/kelos-reviewer.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kelos-reviewer-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kelos Reviewer Agent
@@ -86,6 +84,7 @@ spec:
limits:
ephemeral-storage: "4Gi"
agentConfigRefs:
+ - name: base-agent
- name: kelos-reviewer-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kelos-self-update.yaml b/self-development/kelos-self-update.yaml
index 63c29b6e..9eccfee1 100644
--- a/self-development/kelos-self-update.yaml
+++ b/self-development/kelos-self-update.yaml
@@ -3,8 +3,6 @@ kind: AgentConfig
metadata:
name: kelos-self-update-agent
spec:
- skills:
- - source: gjkim42/kanon-repo
agentsMD: |
# Kelos Self-Update Agent
@@ -61,6 +59,7 @@ spec:
limits:
ephemeral-storage: "2Gi"
agentConfigRefs:
+ - name: base-agent
- name: kelos-self-update-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
@@ -94,7 +93,8 @@ spec:
2. **Configuration Alignment**
- Check that resource requests/limits, models, TTLs, and other settings are appropriate
- Verify that webhook repositories, events, labels, excludeLabels, and filters are correct and consistent
- - Ensure AgentConfig instructions in `agentconfig.yaml` match the project's current conventions in `AGENTS.md`
+ - Ensure every root spawner references `base-agent` first, with role-specific AgentConfigs after it
+ - Ensure `base-agent.yaml` matches `gjkim42/kanon-repo`'s `instructions/AGENTS.md` and shared skills package
- Look for inconsistencies between spawners that should share the same configuration
3. **Workflow Completeness**
diff --git a/self-development/kelos-squash-commits.yaml b/self-development/kelos-squash-commits.yaml
index 635eeedc..5d66358b 100644
--- a/self-development/kelos-squash-commits.yaml
+++ b/self-development/kelos-squash-commits.yaml
@@ -54,6 +54,7 @@ spec:
- name: GIT_COMMITTER_EMAIL
value: "gjkim042@gmail.com"
agentConfigRefs:
+ - name: base-agent
- name: kelos-dev-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kelos-triage.yaml b/self-development/kelos-triage.yaml
index 0b14ef08..7936050d 100644
--- a/self-development/kelos-triage.yaml
+++ b/self-development/kelos-triage.yaml
@@ -44,6 +44,7 @@ spec:
limits:
ephemeral-storage: "2Gi"
agentConfigRefs:
+ - name: base-agent
- name: kelos-dev-agent
ttlSecondsAfterFinished: 864000
podFailurePolicy:
diff --git a/self-development/kelos-workers.yaml b/self-development/kelos-workers.yaml
index c9f149d7..496efe8a 100644
--- a/self-development/kelos-workers.yaml
+++ b/self-development/kelos-workers.yaml
@@ -1,57 +1,4 @@
apiVersion: kelos.dev/v1alpha2
-kind: AgentConfig
-metadata:
- name: kelos-workers-agent
-spec:
- skills:
- - source: gjkim42/kanon-repo
- agentsMD: |
- # Kelos Worker Agent
-
- ## Environment
- You are running in an ephemeral container environment. Your file system
- changes disappear after the task completes. Persist work by creating PRs,
- issues, or comments.
-
- ## Identity
- - When commenting on issues or PRs, always start with "🤖 **Kelos Worker Agent** @gjkim42\n\n"
- so it is clearly distinguishable from human comments and triggers a notification
-
- ## Standards
- - Do not create duplicate issues — check existing issues first with `gh issue list`
- - Keep changes minimal and focused
-
- ## Project Conventions
- - Use Makefile targets instead of discovering build/test commands yourself:
- - `make verify` — run all verification checks (lint, fmt, vet, etc.)
- - `make update` — update all generated files
- - `make test` — run all unit tests
- - `make test-integration` — run integration tests
- - `make build` — build binary
- - When polling for a process to finish in shell, do not use `pgrep -f 'CMD_NAME'`: the caller's own argv contains the pattern, so pgrep matches itself and the loop never exits (deadlocks the pod). Either capture the PID at launch (`cmd & PID=$!`) and poll with `kill -0 "$PID"` / `wait "$PID"`, or exclude self from the match (`pgrep -f 'CMD_NAME' | grep -vw $$`).
- - Always try to add or improve tests when modifying code
- - Logging conventions: start log messages with capital letters and do not end with punctuation
- - Commit messages: do not include PR links in commit messages
- - Kubernetes resource comparison: use semantic `.Equal()` or `.Cmp()` methods for `resource.Quantity` comparisons, not `reflect.DeepEqual`
- - Never use `os.Getenv()` for secrets as Go `flag` defaults: Go's `flag` package prints default values in usage/help output, which leaks secret values; use an empty default and read the env var after `flag.Parse()`
- - Fail fast on invalid configuration: do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing; return an error or exit immediately
- - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped
- - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR.
- - Docs must match implementation, not aspiration: describe only what the code actually does; do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Verify the code enforces a contract before documenting it.
- - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried.
- - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`.
- - Test the happy path, not only early-return guards: when a handler has both guard branches and a primary action (post a message, create a resource, emit a metric), include at least one positive test verifying the primary action runs with the right arguments. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is testable.
- - Avoid vacuous substring assertions in printer/formatter tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing.
- - Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker: if the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative.
- - Qualify cross-CRD field references with the owning kind in docs: in a CRD reference section, write `Task.spec.podOverrides.env` rather than bare `podOverrides.env` when describing a field that lives on a sibling CRD. A reader of one CRD's reference page should be able to locate the cited field without already knowing the layout of the others.
- - PRs that only modify files under `self-development/` are internal agent improvements: use `/kind cleanup` and write "NONE" in the `release-note` block, even when the change fixes a bug or adds a feature in agent behavior. Classify by file location, not by problem nature.
- - TaskSpawner conventions (for `self-development/` YAML files):
- - Prefer webhook-based triggers (`githubWebhook`) over poll-based (`githubPullRequests`) for real-time event-driven tasks
- - The `{{.Branch}}` template variable is empty for issue-only events; use `{{with index . "Branch"}}{{.}}{{else}}main{{end}}` when it may be empty
- - The `issue_comment` webhook event fires for both issues and pull requests; design prompts to detect and handle both contexts
- - Do not include manual PR branch checkout instructions in prompts — Kelos already checks out the PR branch automatically
----
-apiVersion: kelos.dev/v1alpha2
kind: SessionSpawner
metadata:
name: kelos-workers
@@ -66,46 +13,63 @@ spec:
filters:
- event: issue_comment
action: created
- bodyPattern: '(?m)^/kelos pick-up[ \t]*\r?$'
- commentOn: Issue
- state: open
- author: gjkim42
sessionTemplate:
- initialBranch: "kelos-task-{{.Number}}"
+ initialBranch: '{{with index . "Branch"}}{{.}}{{else}}kelos-task-{{.Number}}{{end}}'
initialPrompt: |
- You are a coding agent. You either
- - create a PR to fix the issue
- - update an existing PR to fix the issue
- - comment on the issue or the PR if you cannot fix it
+ You are a coding agent responding to a new GitHub conversation comment. You either
+ - create a PR to address an issue
+ - update an existing PR to address the comment
+ - comment on the issue or PR if no code change is appropriate or you cannot proceed
+
+ Your workspace is backed by a PVC. Files remain available across follow-ups
+ and pod restarts while the Session and its PVC exist. Commit and push work
+ that must outlive the Session.
Webhook:
- Event: {{.Event}}
- Action: {{.Action}}
- Sender: {{.Sender}}
- URL: {{.URL}}
+ - Number: {{.Number}}
+ - Title: {{.Title}}
+ - Branch: {{with index . "Branch"}}{{.}}{{else}}(issue; use kelos-task-{{.Number}}){{end}}
+ - Comment URL: {{.CommentURL}}
+ - Comment body:
+
+ {{.CommentBody}}
+
+
+ Treat the triggering comment as the immediate request in the context of
+ the full issue or PR conversation. `/kelos pick-up` means to take ownership
+ of the complete issue or PR. Other comments may request an incremental
+ change, ask a question, or provide information; do not invent code work
+ when a plain-English response is the appropriate outcome.
Task:
- - 0. Refresh the latest issue state:
- - Re-read the issue and all comments: `gh issue view {{.Number}} --comments`
+ - 0. Determine whether #{{.Number}} is an issue or a pull request and refresh all context:
+ - First try `gh pr view {{.Number}} --comments`. If it succeeds, the triggering subject is that PR. Read all reviews and inline review comments with `gh api` as well.
+ - Otherwise, re-read the issue and all comments with `gh issue view {{.Number}} --comments`, including linked issues and PRs.
- 1. Fetch and rebase on the latest main branch:
```
git fetch --unshallow || true; git fetch origin main; git rebase origin/main
```
- - 2. Check if a PR already exists for branch kelos-task-{{.Number}}.
+ - 2. Identify the work PR:
+ - For a triggering PR comment, use PR #{{.Number}}; Kelos already checked out its head branch.
+ - For a triggering issue comment, check whether a PR already exists for branch kelos-task-{{.Number}}.
- If a PR already exists:
+ If a work PR exists:
- 3a. Read ALL review comments and conversation on the PR (gh pr view, gh api for review comments).
- - Also read ALL comments on the linked issue (gh issue view {{.Number}} --comments), not just the PR.
- - 4a. Read the existing diff (git diff main...HEAD) to understand what has already been done. Do NOT start over or rewrite from scratch.
- - 5a. Make only the incremental changes needed to address review feedback or remaining issues. Preserve existing work.
- - 6a. Commit and push your changes to origin kelos-task-{{.Number}}.
+ - Also read linked issues and their comments, if any, rather than relying only on the PR conversation.
+ - 4a. Read the existing diff (`git diff origin/main...HEAD`) to understand what has already been done. Do NOT start over or rewrite from scratch.
+ - 5a. Address the triggering comment and any valid unresolved feedback with the smallest incremental change. Preserve existing work.
+ - 6a. Commit and push your changes to the current PR branch.
- 7a. Run a local review of the PR diff against `origin/main` using the review tool available in the current environment. If no dedicated review tool is available, manually inspect the diff for correctness. If issues are found, fix them, run `make verify` and `make test`, commit and push, then repeat the local review. Continue until the local review passes.
- - 8a. Update the PR title and description to reflect the final diff. The PR body MUST contain a standard closing keyword reference on its own line (e.g., `Fixes #{{.Number}}` or `Closes #{{.Number}}`). Do not embed the issue number in natural language. Ensure the PR has labels "generated-by-kelos" and "ok-to-test" (use `gh pr edit kelos-task-{{.Number}} --add-label generated-by-kelos --add-label ok-to-test` if missing).
+ - 8a. Update the PR title and description to reflect the final diff. For work originating from an issue, the PR body MUST contain a standard closing keyword reference to that issue on its own line (e.g., `Fixes #{{.Number}}` or `Closes #{{.Number}}`). For a triggering PR comment, preserve its valid issue references and never add a self-closing reference such as `Fixes #{{.Number}}`. Ensure the PR has labels "generated-by-kelos" and "ok-to-test" (use `gh pr edit --add-label generated-by-kelos --add-label ok-to-test` if missing).
- 9a. After pushing, actively check CI status with `gh pr checks`. If any checks fail, investigate the failures, fix them, commit and push, then check again.
- - 10a. If the branch has more than one commit (`[ "$(git rev-list --count origin/main..HEAD)" -gt 1 ]`), squash all commits on the branch into a single commit before requesting external review. Use `git reset --soft $(git merge-base HEAD origin/main)` followed by `git commit` and `git push --force-with-lease origin kelos-task-{{.Number}}`. Do not use `git rebase -i` — interactive commands are not supported. Skip the squash and force-push entirely if the branch is already a single commit, to avoid an unnecessary CI re-run.
- - 11a. After the squash + force-push (if any), actively check CI status with `gh pr checks` again. If any checks fail, investigate the failures, fix them, commit and push, then check again. Do not request external review until CI passes. Then request a review by posting a comment on the PR — `/kelos api-review` if `git diff origin/main...HEAD --name-only` includes any path under `api/` (CRD types, kubebuilder markers, generated CRD YAML), otherwise `/kelos review`. Use `gh pr comment kelos-task-{{.Number}} --body "/kelos api-review"` (or `--body "/kelos review"`). The dedicated reviewer agents will post their findings asynchronously as a sticky PR comment.
+ - 10a. If the branch has more than one commit (`[ "$(git rev-list --count origin/main..HEAD)" -gt 1 ]`), squash all commits on the branch into a single commit before requesting external review. Use `git reset --soft $(git merge-base HEAD origin/main)` followed by `git commit` and `git push --force-with-lease origin HEAD:$(git branch --show-current)`. Do not use `git rebase -i` — interactive commands are not supported. Skip the squash and force-push entirely if the branch is already a single commit, to avoid an unnecessary CI re-run.
+ - 11a. After the squash + force-push (if any), actively check CI status with `gh pr checks` again. If any checks fail, investigate the failures, fix them, commit and push, then check again. Do not request external review until CI passes. Then request a review by posting a comment on the PR — `/kelos api-review` if `git diff origin/main...HEAD --name-only` includes any path under `api/` (CRD types, kubebuilder markers, generated CRD YAML), otherwise `/kelos review`. Use `gh pr comment --body "/kelos api-review"` (or `--body "/kelos review"`). The dedicated reviewer agents will post their findings asynchronously as a sticky PR comment.
- If no PR exists:
+ If no work PR exists (issue comments only):
- 3b. Investigate the issue #{{.Number}}.
- Read ALL comments on the issue (gh issue view {{.Number}} --comments), including maintainer feedback and linked issues/PRs.
- If previous PRs for this issue were closed, read their reviews to understand why before choosing your approach.
@@ -121,7 +85,7 @@ spec:
- 11b. After the squash + force-push (if any), actively check CI status with `gh pr checks` again. If any checks fail, investigate the failures, fix them, commit and push, then check again. Do not request external review until CI passes. Then request a review by posting a comment on the PR — `/kelos api-review` if `git diff origin/main...HEAD --name-only` includes any path under `api/` (CRD types, kubebuilder markers, generated CRD YAML), otherwise `/kelos review`. Use `gh pr comment kelos-task-{{.Number}} --body "/kelos api-review"` (or `--body "/kelos review"`). The dedicated reviewer agents will post their findings asynchronously as a sticky PR comment.
Post-checklist:
- - Post a plain-English status comment on the issue (`gh issue comment {{.Number}} --body "..."`) for any of the following situations, explaining what happened:
+ - Post a plain-English status comment on the source issue or PR (`gh issue comment {{.Number}} --body "..."`) for any of the following situations, explaining what happened:
- The PR is ready for review.
- You commented on the issue or the PR for more information.
- You cannot make any progress on the issue, explain why.
@@ -164,4 +128,4 @@ spec:
- name: GIT_COMMITTER_EMAIL
value: "gjkim042@gmail.com"
agentConfigRefs:
- - name: kelos-workers-agent
+ - name: base-agent
diff --git a/self-development/tasks/fake-strategist-task.yaml b/self-development/tasks/fake-strategist-task.yaml
index 98fbbc83..dee09051 100644
--- a/self-development/tasks/fake-strategist-task.yaml
+++ b/self-development/tasks/fake-strategist-task.yaml
@@ -16,6 +16,7 @@ spec:
secretRef:
name: kelos-credentials
agentConfigRefs:
+ - name: base-agent
- name: kelos-fake-strategist-agent
podOverrides:
resources: