Skip to content
Merged
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ KORA_CRDS_CHART ?= $(KORA_CHARTS)/kora-crds --version $(KORA_VERSION)
SCHEMAS := .schemas

# The kustomizations make validate builds, one per example repository.
KUSTOMIZATIONS := single-tenant/platform single-tenant/tenant multi-tenant/platform multi-tenant/engineering multi-tenant/sre factories/autofix/platform factories/autofix/factory factories/sre/platform factories/sre/factory factories/sre/demo
KUSTOMIZATIONS := single-tenant/platform single-tenant/tenant multi-tenant/platform multi-tenant/engineering multi-tenant/sre factories/autofix/platform factories/autofix/factory factories/sre/platform factories/sre/factory factories/sre/demo factories/feature/platform factories/feature/factory

KUBECONFORM := kubeconform -strict -summary \
-schema-location default \
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ An example is a set of Kubernetes manifests that declare tenants, models, agents
* [`multi-tenant`](multi-tenant): An engineering team and an SRE team, each in a separate tenant and repository.
* [`factories/autofix`](factories/autofix): A factory of four agents that finds, verifies and fixes problems in a GitHub repository and hands a person the pull request.
* [`factories/sre`](factories/sre): A factory that triages alerts in the cluster, sweeps its costs with OpenCost and proposes fixes as pull requests.
* [`factories/feature`](factories/feature): A factory that turns a feature request into an approved spec, splits it into tasks and builds each task in a pull request.

Each example splits its manifests by owner:

Expand Down
2 changes: 1 addition & 1 deletion factories/autofix/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Gate A is on in the example. To switch to gate B, remove `triggers/fix-verified.
`make example` checks the key file, the owner and the placeholders first, and lists anything missing.
5. Run `make tunnel` and add a webhook to your repository with the URL and secret it prints. Select the issues, issue comments, pull requests, pull request reviews and workflow runs events. See [Receive webhooks locally](../../quickstart/README.md#receive-webhooks-locally).

Sign in as `admin@autofix.local.runkora.io` to watch the factory's runs. `maintainer@autofix.local.runkora.io` can see the agents, triggers and runs.
Sign in as `autofix-admin@local.runkora.io` to watch the factory's runs. `autofix-maintainer@local.runkora.io` can see the agents, triggers and runs.

## Run it on your own cluster

Expand Down
11 changes: 8 additions & 3 deletions factories/autofix/platform/tenants/autofix.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@ spec:
displayName: Autofix
active: true
oidc:
issuerURL: https://dex.local.runkora.io:5557/dex
issuerURL: http://dex.localhost:5557/dex
clientID: kora-quickstart
scopes: [openid, profile, email, groups]
# The console routes a person to this tenant by the domain of their email.
# Every example tenant claims local.runkora.io. kora signs a person in to
# the tenant whose group is in their groups claim.
emailDomains:
- autofix.local.runkora.io
- local.runkora.io
claimBinding:
claim: groups
values:
- tenant-autofix
apiAuthoring: Enabled
# The tenant's spending limit across every agent and person.
budget:
Expand Down
60 changes: 60 additions & 0 deletions factories/feature/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Feature factory

The feature factory is an example of a lights-off feature delivery loop in runkora. A person labels a feature request, and `spec-writer` opens the spec as a pull request. A person approves the spec by merging it. `captain` splits the spec into tasks and starts an `implementer` run for each one. Each task pull request goes through `reviewer`, CI and `verifier` until it is ready for a person to merge. When every task has merged, `captain` marks the feature done.

## How the factory works

1. **Spec:** A person labels an issue `ready-to-spec`. `spec-writer` writes `specs/<issue>.md` with the tasks and acceptance criteria, and opens a pull request on `spec/<issue>`.
2. **Approve:** A person reviews the spec and merges it.
3. **Plan:** `captain` posts a checklist of the tasks on the issue and starts an `implementer` run for each task that is ready.
4. **Build:** `implementer` builds its task with tests and opens a pull request on `feature/<issue>-<task>`.
5. **Check:** `reviewer` reviews each push. When CI passes, `verifier` checks the acceptance criteria. Failed CI, reviews and verifications go back to `implementer`, which joins its open run.
6. **Merge:** A person merges each task. `captain` ticks it off and starts the tasks that were waiting on it.

The implementer can push to the repository, and pushing also allows merging. Protect your default branch with a required approving review. The factory cannot approve its own pull requests.

## How the captain starts other agents

`captain` starts runs with the `runs:start` tool on the kora MCP server. The server checks the captain's own grant, and the captain's `scopes` allow it to start and read `implementer` runs and no other agent's. The checklist on the issue holds its progress. A merged task and a task labelled `needs-human` both wake it again.

## What each agent can reach

Each agent is set up with an example configuration:

| Agent | Model | kora | GitHub actions | Monthly budget |
| --- | --- | --- | --- | --- |
| `spec-writer` | `anthropic/claude-haiku-5-5` | None | `read`, `contents:write`, `pulls:write`, `issues:write` | $10 |
| `captain` | `anthropic/claude-haiku-5-5` | `agents:list`, `runs:start`, `runs:get`, `runs:list` on `implementer` | `read`, `issues:write` | $10 |
| `implementer` | `anthropic/claude-sonnet-5-5` | None | `read`, `contents:write`, `pulls:write` | $40 |
| `reviewer` | `anthropic/claude-haiku-5-5` | None | `read`, `pulls:write` | $20 |
| `verifier` | `anthropic/claude-haiku-5-5` | None | `read`, `pulls:write` | $20 |

## Adapt the agents to your repository

The prompts are general. Teach each agent about your repository: how to build and test it, the conventions you follow and what a good spec looks like for your team.

## Set it up

1. Create a GitHub App with read and write access to contents, issues and pull requests, install it on your repository and generate a private key.
2. In `factory/shared/github-connection.yaml`, set `app_id`, `installation_id` and `owner`.
3. In every file in `factory/triggers/`, replace `example-org/example-repo` with your repository and `example-feature[bot]` with your App's bot user `<app-slug>[bot]`.
4. On the quickstart, set `GITHUB_APP_PRIVATE_KEY_FILE` in `quickstart/.env`, uncomment `quickstart/providers/openrouter.yaml` and run:

```bash
make credentials providers example EXAMPLE=factories/feature
```

`make example` lists anything missing first.
5. Run `make tunnel` and add a webhook to your repository with the URL and secret it prints. Select the issues, pull requests, pull request reviews and workflow runs events. See [Receive webhooks locally](../../quickstart/README.md#receive-webhooks-locally).
6. Open an issue for a feature and add the label `ready-to-spec`.

Sign in as `feature-admin@local.runkora.io` to watch the runs. `feature-maintainer@local.runkora.io` can see the agents, triggers and runs.

## Run it on your own cluster

1. Copy `platform/` and `factory/` into the repositories your GitOps tool reads.
2. In `platform/tenants/feature.yaml`, replace the `oidc` block with your identity provider's client.
3. In `platform/models/`, point each route at an `InferenceProvider` you registered.
4. Create a `ClusterSecretStore` named `example-secret-store`. Store the App's private key under `feature-github-app` and the webhook secret under `feature-github-webhook`.

See [Declarative resources](https://github.com/runkora/kora/blob/main/docs/guides/declarative-resources.md) and [Create a trigger](https://github.com/runkora/kora/blob/main/docs/guides/create-a-trigger.md) for the resources the factory uses.
60 changes: 60 additions & 0 deletions factories/feature/factory/agents/captain.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
apiVersion: runkora.io/v1alpha1
kind: Agent
metadata:
name: captain
spec:
displayName: Captain
description: Splits an approved spec into tasks, starts an implementer run for each task and tracks the feature to done on its issue.
identityMode: autonomous
model:
family: anthropic
name: claude-haiku-5-5
systemPrompt: |
You deliver one feature from its approved spec. You split it into tasks,
start an implementer run for each task and see every task through until
a person merges it.

Triggers already send every review and every failed CI run on a task pull
request to the implementer that owns it. You do not relay these. You step
in when a task stalls.

When a person merges the spec:
1. Read specs/<issue>.md and its tasks.
2. Post one checklist comment on the feature issue. List each task, what
it depends on and its state.
3. Find the implementer's agent_type_id with agents.list. Start an
implementer run with runs.start for each task with no open dependency.
In the initial message, give the repository, the issue, the spec path,
the task and the branch feature/<issue>-<task number>.
4. Add each run ID to the checklist.

Each time you are woken, read the checklist first. It is your record.

When a person merges a task pull request, tick the task. Start every task
whose dependencies are now merged, and add their run IDs.

When a task needs help, check its run with runs.get:
* If the run failed or ended before a pull request opened, start a new
implementer run for the task and record the new run ID.
* If the pull request is labelled needs-human, comment on the feature
issue with what remains and mark the task blocked on the checklist.

When a person has merged every task, comment on the feature issue that
the feature is done.
environments:
- name: build
default: true
# The captain starts and reads implementer runs, and no other agent's.
mcpTools:
- server: kora
tools: [agents:list, runs:start, runs:get, runs:list]
scopes:
- agents:types:implementer:read
- agents:runs:implementer:read
- agents:runs:implementer:write
connectors:
- connection: github
actions: [read, issues:write]
budget:
limitUSD: 10
resetCadence: monthly
37 changes: 37 additions & 0 deletions factories/feature/factory/agents/implementer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
apiVersion: runkora.io/v1alpha1
kind: Agent
metadata:
name: implementer
spec:
displayName: Implementer
description: Builds one task of an approved spec in a pull request and answers review, verification and failed CI on its own pull requests.
identityMode: autonomous
model:
family: anthropic
name: claude-sonnet-5-5
systemPrompt: |
You build one task of an approved spec.

For a task from the captain:
1. Read the spec and the task. Clone the repository.
2. Write tests for the task's acceptance criteria, then build it, run
the tests and push the branch the captain gave you.
3. Open a pull request whose body starts with "Refs #<issue>", names the
task and lists the acceptance criteria it meets.

For a review, a verification or a failed CI run on your pull request:
1. Read every review, every comment and the latest CI run.
2. Fix what they ask for, run the tests and push to the same branch.

Count your pushes after the first. When review, verification or CI still
fails after your third, comment what remains, add the label needs-human
and stop. Do not merge.
environments:
- name: build
default: true
connectors:
- connection: github
actions: [read, contents:write, pulls:write]
budget:
limitUSD: 40
resetCadence: monthly
31 changes: 31 additions & 0 deletions factories/feature/factory/agents/reviewer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
apiVersion: runkora.io/v1alpha1
kind: Agent
metadata:
name: reviewer
spec:
displayName: Reviewer
description: Reviews each task pull request against its spec and states a verdict.
identityMode: autonomous
model:
family: anthropic
name: claude-haiku-5-5
systemPrompt: |
You review the task pull request you are given.

1. Read the spec it refers to, the diff and the latest CI run.
2. Report correctness problems first, then gaps against the spec, then
missing tests. Comment on the lines each problem is on.
3. Submit your review with event COMMENT. The implementer opens pull
requests as the same GitHub App you post as, and GitHub rejects
APPROVE and REQUEST_CHANGES from a pull request's own author.
4. End the review body with one verdict line: "Verdict: would approve" or
"Verdict: would request changes".
environments:
- name: build
default: true
connectors:
- connection: github
actions: [read, pulls:write]
budget:
limitUSD: 20
resetCadence: monthly
33 changes: 33 additions & 0 deletions factories/feature/factory/agents/spec-writer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
apiVersion: runkora.io/v1alpha1
kind: Agent
metadata:
name: spec-writer
spec:
displayName: Spec writer
description: Turns a feature request issue into a spec with acceptance criteria and opens it as a pull request for a person to approve.
identityMode: autonomous
model:
family: anthropic
name: claude-haiku-5-5
systemPrompt: |
You write the spec for the feature request issue you are given.

1. Read the issue and the code it touches.
2. Write specs/<issue>.md with the goal, the design, the tasks in the
order they can merge and numbered acceptance criteria. Each criterion
is a check someone can run.
3. Push a branch named spec/<issue> and open a pull request whose body
starts with "Refs #<issue>".
4. Comment on the issue with a link to the pull request.

For a review or a comment on your pull request, update the spec and push
to the same branch. Do not merge.
environments:
- name: build
default: true
connectors:
- connection: github
actions: [read, contents:write, pulls:write, issues:write]
budget:
limitUSD: 10
resetCadence: monthly
29 changes: 29 additions & 0 deletions factories/feature/factory/agents/verifier.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
apiVersion: runkora.io/v1alpha1
kind: Agent
metadata:
name: verifier
spec:
displayName: Verifier
description: Checks a task pull request with passing CI against each acceptance criterion in its spec.
identityMode: autonomous
model:
family: anthropic
name: claude-haiku-5-5
systemPrompt: |
You verify the task pull request you are given against its spec.

1. Clone the repository and check out the pull request's branch.
2. Run each acceptance criterion the task claims, and record what you
ran and what you saw.
3. Submit a review with event COMMENT. Start the body with
"Verification passed" or "Verification failed", then list each
criterion with its result.
environments:
- name: build
default: true
connectors:
- connection: github
actions: [read, pulls:write]
budget:
limitUSD: 20
resetCadence: monthly
32 changes: 32 additions & 0 deletions factories/feature/factory/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# The feature factory: a tenant team's repository that turns a feature request
# into a spec, splits it into tasks and builds each task in a pull request.
# Point your GitOps tool at this folder, or apply it with kubectl apply -k.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# The factory's tenant namespace. The resource files leave namespace out.
namespace: kora-tenant-feature
commonAnnotations:
# The console shows this value as "Managed by" on each declared item.
runkora.io/repository: github.com/runkora/examples
# Applies the resources in the order below. The captain's scopes name the
# implementer, which must exist first.
sortOptions:
order: fifo
resources:
- shared/github-connection.yaml
- shared/github-source.yaml
- shared/build-environment.yaml
- shared/maintainer-role.yaml
- agents/implementer.yaml
- agents/spec-writer.yaml
- agents/reviewer.yaml
- agents/verifier.yaml
- agents/captain.yaml
- triggers/spec-on-ready.yaml
- triggers/plan-on-spec-merged.yaml
- triggers/next-on-task-merged.yaml
- triggers/captain-on-needs-human.yaml
- triggers/review-task-pr.yaml
- triggers/verify-on-ci-pass.yaml
- triggers/implementer-on-ci.yaml
- triggers/implementer-on-review.yaml
16 changes: 16 additions & 0 deletions factories/feature/factory/shared/build-environment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# The sandbox the agents clone, build and test the target repository in.
# Set sandboxImage to the bundled image for your repository's language:
# kora-base, kora-go, kora-python or kora-node.
apiVersion: runkora.io/v1alpha1
kind: Environment
metadata:
name: build
spec:
displayName: Build
description: Clones, builds and tests the target repository.
sandboxImage: kora-base
# Each run gets its own sandbox of this size. Raise it for a large repository.
size:
cpu: 500m
memory: 1Gi
disk: 5Gi
37 changes: 37 additions & 0 deletions factories/feature/factory/shared/github-connection.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# The factory's GitHub App installation. Store the App's private key under the
# remote key below and set the config values from the App's settings. See
# https://github.com/runkora/kora/blob/main/docs/guides/register-connection.md.
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: github-app
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: example-secret-store
target:
name: github-app
data:
- secretKey: key.pem
remoteRef:
key: feature-github-app
property: key.pem
---
apiVersion: runkora.io/v1alpha1
kind: Connection
metadata:
name: github
spec:
adapter: github_app
baseHost: github.com
secretRef: github-app
secretKey: key.pem
config:
# The App ID from the App's settings page.
app_id: "123456"
# The number at the end of the installation's settings URL.
installation_id: 12345678
# The organisation the App is installed on. kora denies requests for any
# other owner.
owner: example-org
Loading
Loading