Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 110 additions & 2 deletions internal/examples/self_development_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,115 @@ func TestKelosAPIReviewerTriggerableByBot(t *testing.T) {
assertReviewerTriggerableByBot(t, "kelos-api-reviewer.yaml", "/kelos api-review")
}

func TestKanonReviewerAcceptsAPIReviewAlias(t *testing.T) {
t.Parallel()

ts := readDevelopmentTaskSpawner(t, "kanon-development", "kanon-reviewer.yaml")
spawner := ts.Spec.When.GitHubWebhook
if spawner == nil {
t.Fatalf("expected kanon-reviewer.yaml to use githubWebhook")
}

tests := []struct {
name string
eventType string
payload string
want bool
}{
{
name: "issue comment api-review alias",
eventType: "issue_comment",
payload: `{
"action": "created",
"sender": {"login": "gjkim42"},
"issue": {
"number": 1,
"state": "open",
"pull_request": {"url": "https://api.github.com/repos/kelos-dev/kanon/pulls/1"}
},
"comment": {"body": "/kelos api-review"},
"repository": {"full_name": "kelos-dev/kanon", "owner": {"login": "kelos-dev"}, "name": "kanon"}
}`,
want: true,
},
{
name: "issue comment original review command",
eventType: "issue_comment",
payload: `{
"action": "created",
"sender": {"login": "gjkim42"},
"issue": {
"number": 1,
"state": "open",
"pull_request": {"url": "https://api.github.com/repos/kelos-dev/kanon/pulls/1"}
},
"comment": {"body": "/kelos review"},
"repository": {"full_name": "kelos-dev/kanon", "owner": {"login": "kelos-dev"}, "name": "kanon"}
}`,
want: true,
},
{
name: "pull request review api-review alias",
eventType: "pull_request_review",
payload: `{
"action": "submitted",
"sender": {"login": "gjkim42"},
"pull_request": {
"number": 1,
"state": "open",
"html_url": "https://github.com/kelos-dev/kanon/pull/1",
"head": {"ref": "feature-branch"}
},
"review": {"body": "/kelos api-review"},
"repository": {"full_name": "kelos-dev/kanon", "owner": {"login": "kelos-dev"}, "name": "kanon"}
}`,
want: true,
},
{
name: "kelos bot remains excluded",
eventType: "issue_comment",
payload: `{
"action": "created",
"sender": {"login": "kelos-bot[bot]"},
"issue": {
"number": 1,
"state": "open",
"pull_request": {"url": "https://api.github.com/repos/kelos-dev/kanon/pulls/1"}
},
"comment": {"body": "/kelos api-review"},
"repository": {"full_name": "kelos-dev/kanon", "owner": {"login": "kelos-dev"}, "name": "kanon"}
}`,
want: false,
},
}

for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

eventData, err := webhook.ParseGitHubWebhook(tt.eventType, []byte(tt.payload))
if err != nil {
t.Fatalf("ParseGitHubWebhook() error = %v", err)
}
got, err := webhook.MatchesGitHubEvent(spawner, tt.eventType, eventData)
if err != nil {
t.Fatalf("MatchesGitHubEvent() error = %v", err)
}
if got != tt.want {
t.Fatalf("MatchesGitHubEvent() = %v, want %v", got, tt.want)
}
})
}
}

// assertReviewerTriggerableByBot checks that the given reviewer spawner triggers
// for both the Kelos bot and a maintainer posting bodyPattern on an open PR, but
// not for an unauthorized user.
func assertReviewerTriggerableByBot(t *testing.T, file, bodyPattern string) {
t.Helper()

ts := readSelfDevelopmentTaskSpawner(t, file)
ts := readDevelopmentTaskSpawner(t, "self-development", file)
spawner := ts.Spec.When.GitHubWebhook
if spawner == nil {
t.Fatalf("expected %s to use githubWebhook", file)
Expand Down Expand Up @@ -139,7 +241,13 @@ func assertReviewerTriggerableByBot(t *testing.T, file, bodyPattern string) {
func readSelfDevelopmentTaskSpawner(t *testing.T, file string) *kelosv1alpha1.TaskSpawner {
t.Helper()

path := filepath.Join("..", "..", "self-development", file)
return readDevelopmentTaskSpawner(t, "self-development", file)
}

func readDevelopmentTaskSpawner(t *testing.T, dir, file string) *kelosv1alpha1.TaskSpawner {
t.Helper()

path := filepath.Join("..", "..", dir, file)
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("reading %s: %v", path, err)
Expand Down
19 changes: 12 additions & 7 deletions kanon-development/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ maintain (`kanon-development/*`) live in *this* repository, so they use the
|---|---|---|---|
| **kanon-workers** | Webhook: issue comment `/kelos pick-up` | Codex | Picks up issues, creates or updates PRs, self-reviews, and ensures CI passes |
| **kanon-planner** | Webhook: issue comment `/kelos plan` | Codex | Investigates an issue and posts a structured implementation plan — advisory only, no code changes |
| **kanon-reviewer** | Webhook: PR comment `/kelos review` | Codex | Reviews PRs on demand — analyzes code, checks conventions, and submits structured reviews |
| **kanon-reviewer** | Webhook: PR comment/review with `/kelos review` or `/kelos api-review` | Codex | Reviews PRs on demand — analyzes code, checks conventions, and submits structured reviews |
| **kanon-pr-responder** | Webhook: PR review/comment with `/kelos pick-up` | Codex | Re-engages on PR review feedback and updates the existing branch incrementally |
| **kanon-triage** | Webhook: issue opened/reopened (untriaged) | Codex | Classifies issues by kind/priority, detects duplicates, and recommends an actor |
| **kanon-fake-user** | Cron (daily 09:00 UTC) | Codex | Tests DX as a new user — follows docs, tries CLI workflows, files issues for problems found |
Expand All @@ -41,8 +41,13 @@ maintain (`kanon-development/*`) live in *this* repository, so they use the
| **kanon-self-update** | Cron (daily 06:00 UTC) | Codex | Reviews and tunes the `kanon-development/` prompts, configs, and README — the pipeline improves itself |
| **kanon-squash-commits** | Webhook: PR comment `/kelos squash-commits` | Codex | Rebases and squashes PR branch commits into a single clean commit |

> **Not ported from `self-development/`:** `kelos-api-reviewer` (Kanon has no
> Kubernetes CRDs/API surface to review) and `kelos-image-update` (Kanon has no
> **Not ported from `self-development/`:** `kelos-api-reviewer` — Kanon has no
> Kubernetes CRDs, so there is no separate API-review spawner. Kanon's
> user-facing API surface (CLI commands/flags and the `kanon.yaml` schema) is
> reviewed by `kanon-reviewer` directly (see its "CLI and config surfaces are
> special" section), and triaged via the `kind/api` label. `/kelos api-review`
> is accepted as an alias for `/kelos review` on Kanon PRs; both commands run
> the same reviewer. `kelos-image-update` is also not ported (Kanon has no
> coding-agent Dockerfiles to bump).

Apply the whole directory at once — this includes `agentconfig.yaml`, which
Expand Down Expand Up @@ -99,11 +104,11 @@ kubectl apply -f kanon-development/kanon-planner.yaml

### kanon-reviewer.yaml

Reviews open pull requests on demand when a maintainer posts `/kelos review`.
Reviews open pull requests on demand when a maintainer posts `/kelos review` or `/kelos api-review`.

| | |
|---|---|
| **Trigger** | GitHub PR comment webhook with `/kelos review` |
| **Trigger** | GitHub PR comment/review webhook with `/kelos review` or `/kelos api-review` |
| **Agent** | Codex |
| **Concurrency** | 3 |

Expand All @@ -116,8 +121,8 @@ Reviews open pull requests on demand when a maintainer posts `/kelos review`.
- Read-only agent — does not push code, modify files, or run local validation

**Handoff flow:**
1. `/kelos review` — requests a code review on the PR
2. `/kelos review` — maintainer can retrigger review after changes are pushed
1. `/kelos review` or `/kelos api-review` — requests a code review on the PR
2. `/kelos review` or `/kelos api-review` — maintainer can retrigger review after changes are pushed

**Deploy:**
```bash
Expand Down
4 changes: 2 additions & 2 deletions kanon-development/kanon-reviewer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ spec:
filters:
- event: issue_comment
action: created
bodyPattern: /kelos review
bodyPattern: /kelos (api-)?review
commentOn: PullRequest
state: open
author: gjkim42
- event: pull_request_review
action: submitted
bodyPattern: /kelos review
bodyPattern: /kelos (api-)?review
state: open
author: gjkim42
reporting:
Expand Down
Loading