Onboarding a New Bot Instance¶
How to add a new bot runner instance to the shared OpenShift cluster. Each instance gets its own Jira label, repo set, and personas, but shares the memory server, database, and Vault secrets deployed by the primary instance (platform-frontend-ai-dev). Instances share the proxy by default, but can optionally deploy their own proxy for custom Jira credentials.
For the system architecture, see ARCHITECTURE.md.
Prerequisites¶
Before starting, you need:
- [ ] A GitHub repo for your instance (e.g.
RedHatInsights/my-bot-instance) - [ ] A Jira label for your team's tickets (e.g.
hcc-ai-myteam) - [ ] Access to the app-interface repo (
gitlab.cee.redhat.com/service/app-interface) - [ ] The primary instance (platform-frontend-ai-dev) already deployed in the target namespace — it provides the shared proxy, memory server, and secrets
Step 1: Create the Runner Repo¶
The runner repo uses dev-bot as a git submodule. It contains only instance-specific config — no bot code.
mkdir my-bot-instance && cd my-bot-instance
git init
git submodule add https://github.com/RedHatInsights/platform-frontend-ai-dev.git dev-bot
Create the required files:
setup.sh¶
Runs as root during the Docker build, after preset installs. Install instance-specific packages here. Each custom DNF install must explicitly select UBI repos; Dockerfile repo flags do not carry into setup.sh.
#!/bin/bash
set -e
echo "my-bot-instance" > /home/botuser/app/.instance-id
# Instance-specific packages go here:
# dnf --disableplugin=subscription-manager --disablerepo='*' --enablerepo='ubi-*' install -y --nodocs <package>
# pip3.12 install <package>
# npm install -g <package>
echo "Instance setup complete: my-bot-instance"
instance/ directory¶
Create your instance config. This entire directory gets COPYed into the image at /home/botuser/app/instance/. The bot loads it at startup via BOT_CONFIG_PATH.
instance/my-config/
└── agent/
├── instance.yaml # workflow, env presets, runtime/provider, CLAUDE.md strategy
├── CLAUDE.md # instance-specific instructions (optional, strategy-dependent)
├── project-repos.json # repos this instance works on
├── mcp.json # MCP server overrides (usually just Jira)
├── personas/ # domain-specific guidelines
│ ├── frontend/
│ │ └── prompt.md
│ └── ...
├── extra-hosts # custom /etc/hosts entries for browser preset (optional)
├── preflight/ # instance-specific preflight scripts (optional)
│ └── 01-check-something.py
└── workflows/ # custom workflows (optional, if not using a built-in)
└── my-workflow/
├── CLAUDE.md
├── manifest.yaml
└── preflight/
└── 01-check.py
Instance-level preflight/ scripts run alongside the workflow's preflight scripts — use them for checks specific to your instance. Custom workflows/ are referenced via workflow: ./workflows/<name> in instance.yaml. See Creating Custom Workflows and Writing Custom Preflight Scripts for details.
instance.yaml¶
Declares which workflow and env presets your instance uses. Required for all new instances.
workflow: jira-sprint
source: jira
envs:
- browser
- slack
- container-scan
runtime: claude
provider: vertex
| Field | Type | Default | Description |
|---|---|---|---|
workflow |
string | jira-sprint |
Workflow preset name (built-in) or ./path (custom). See below. |
source |
string | jira |
Ticket source. jira = Jira sprint polling. scheduled = time-based. |
envs |
list or null | null (all) |
Env presets to activate. null/omitted = all available. [] = none. |
claude_md.strategy |
string | ignore |
How to handle instance CLAUDE.md: ignore, append, replace. |
runtime |
string | claude |
Runtime adapter: claude or opencode (V2 only). |
provider |
string | Runtime-dependent | vertex (Claude via Vertex auth proxy), rehor-openai (native Responses), or rehor-openai-chat (Chat Completions). Defaults to vertex for Claude and rehor-openai for OpenCode. |
model |
string or null | null |
Optional model override (e.g. claude-sonnet-4-6). Must be allowed by the selected provider route. |
runtime and provider are selected independently and passed to the TypeScript
coordinator. Supported combinations are claude + vertex, opencode +
vertex, opencode + rehor-openai (native Responses), and
opencode + rehor-openai-chat (Chat Completions). OpenCode+rehor-openai
uses config.json's opencode.model (gpt-6-luna); OpenCode+Vertex preserves
claude.model and uses the existing Vertex proxy. Chat Completions requires a
model declared under its provider. OpenCode+Vertex supports the approved Claude
Sonnet 4.6, Opus 4.6, and Haiku 4.5 model IDs; the proxy enforces
VERTEX_ALLOWED_MODELS and owns the service-account credential.
Invalid combinations fail validation. If
omitted from instance.yaml, deployment operators can set BOT_RUNTIME and
BOT_PROVIDER; the default remains the Claude/Vertex path. Keep that path
available as the rollback while running the OpenCode canary
runbook.
OpenCode uses client/CLI 2.1.2 with no versioned runtime aliases. Migrate existing Claude SDK instances directly to V2 using the migration steps. The experimental onboarding consumer follows in a separate PR only after the main runtime PR merges, pinning its gitlink to that merged commit and adopting native V2 profile settings.
runtime and provider select the agent adapter and provider route inside the
coordinator; they do not switch the runner engine.
BOT_EXECUTION_ENGINE is a separate setting on the runner container (in the
SandboxTemplate): python (the default) uses the legacy Python runner, while
coordinator opts that runner into the TypeScript coordinator. Step 4 shows how to scope this
setting to one app-interface deployment.
Workflows: The built-in jira-sprint workflow handles the full autonomous development loop (triage → implement → PR → maintain). For specialized use cases — monitoring, review-only, scheduled tasks — you can create custom workflows in your instance config repo using workflow: ./workflows/<name>. See Creating Custom Workflows for the full guide.
Env presets add tools and runtimes to the bot image. List only what your instance needs — unused presets waste build time and image size.
| Preset | What it provides |
|---|---|
node |
nvm + Node.js 24 LTS + npm/npx |
go |
goenv + Go 1.24/1.25 + golangci-lint |
python |
pyenv + Python 3.12.8 + ruff + mypy |
patternfly-mcp |
PatternFly component guidance MCP server (requires node) |
browser |
Chromium + chrome-devtools MCP for visual verification |
container-scan |
Grype + Buildah for CVE scanning |
dev-proxy |
Caddy reverse proxy for stage UI verification |
slack |
Slack notifications via webhook |
For the full preset system reference, see the Presets overview, env presets, and workflow presets.
project-repos.json¶
List only the repos your instance should work on:
{
"my-frontend": {
"url": "https://github.com/your-bot-fork/my-frontend.git",
"upstream": "https://github.com/YourOrg/my-frontend.git"
},
"my-backend": {
"url": "https://github.com/your-bot-fork/my-backend.git",
"upstream": "https://github.com/YourOrg/my-backend.git"
},
"app-interface": {
"url": "https://gitlab.cee.redhat.com/your-bot-fork/app-interface.git",
"upstream": "https://gitlab.cee.redhat.com/service/app-interface.git",
"host": "gitlab"
}
}
url— bot's fork (where it pushes branches)upstream— original repo (PR/MR target)host— set to"gitlab"for GitLab repos (default: GitHub)readonly— set totrueif bot should only read, never push
mcp.json¶
Typically just points to the shared Jira MCP server:
Personas¶
Copy and adapt from dev-bot/rehor-config/agent/personas/. Each persona is a prompt.md with coding standards, test commands, and conventions for that repo type.
README.md¶
# my-bot-instance
Custom bot runner built on [dev-bot](https://github.com/RedHatInsights/platform-frontend-ai-dev).
## Build
\`\`\`bash
git submodule update --init --recursive
docker build -f dev-bot/Dockerfile.runner -t my-bot-instance:local .
\`\`\`
## Updating dev-bot
\`\`\`bash
cd dev-bot && git pull origin master && cd ..
git add dev-bot
git commit -m "chore: update dev-bot submodule"
\`\`\`
Reference PRs¶
- Bootstrap runner with dev-bot submodule — initial repo setup
- Add UI instance config — project-repos.json, personas, mcp.json
Step 2: Deploy Template¶
Create deploy/template.yaml from deploy/sandbox-template.example.yaml. This is an OpenShell SandboxTemplate + SandboxWarmPool template — it does NOT create the memory server or proxy (those come from the primary instance).
Copy deploy/sandbox-template.example.yaml and adjust:
metadata.name— your instance name- Default
BOT_NAME— e.g.devbot-myteam - Default
BOT_LABEL— your Jira label BOT_IMAGEandBOT_IMAGE_DIGEST— your Quay image and immutable digest
The template creates these resources:
1. SandboxTemplate — OpenShell pod blueprint with managed network policy and shared proxy/memory-server wiring.
2. SandboxWarmPool — OpenShell running-bot pool; one replica starts one bot.
3. ScaledObject — KEDA cron schedule targeting SandboxWarmPool.
OpenShell prerequisites:
- Shared platform namespace: OpenShell gateway, agent-sandbox controller, CRDs, and KEDA are already installed. Use existing shared infrastructure.
- Standalone namespace: install OpenShell gateway, agent-sandbox controller, extensions.agents.x-k8s.io/v1beta1 SandboxTemplate/SandboxWarmPool CRDs, and KEDA before applying this template.
- Grant image-pull access to bot image if Quay repository is private.
- Deploy shared devbot-proxy, devbot-memory-server, and devbot-secrets first.
- Keep BOT_IMAGE_DIGEST pinned in app-interface.
- Add SandboxTemplate.agents.x-k8s.io, SandboxWarmPool.agents.x-k8s.io, ScaledObject.keda.sh, and NetworkPolicy to managedResourceTypes.
Sandbox filesystem contract:
- /home/botuser/app: runner code and config; agent reads, bootstrap may update runtime files.
- /home/botuser/repos: cloned target repos; agent read/write.
- /home/botuser/data: caches, cycle state, merged config, reports; agent read/write.
- /tmp: temporary files; agent read/write.
- System paths read-only. Credential dirs denied. See presets/core/CLAUDE.md section OpenShell Filesystem Access.
Key environment variables (already wired in the template):
- BOT_MEMORY_URL=http://devbot-memory-server:8080/mcp — shared memory server
- EXECUTOR_ADDR=devbot-proxy:9090 — executor
- HTTP_PROXY=http://devbot-proxy:3128 — Squid proxy
- JIRA_MCP_URL=http://devbot-proxy:8444/mcp — Jira MCP
Shared Proxy¶
| Mode | PROXY_NAME |
JIRA_SECRET_NAME |
When to use |
|---|---|---|---|
| Shared (default) | devbot-proxy |
devbot-secrets |
Same Jira identity as primary instance |
| Per-instance | unique name | your secret name | Custom Jira credentials; deploy proxy separately |
Why a separate proxy? Bot pods are network-isolated — the NetworkPolicy only allows egress to the proxy and memory server. The proxy runs mcp-atlassian (Jira MCP server) with the Jira credentials baked in. To use a different Jira account, you need a separate proxy pod with different credentials. A sidecar won't work because pods in the same deployment share the same NetworkPolicy.
NetworkPolicy proxy label — Important¶
The NetworkPolicy must use app.kubernetes.io/name: devbot-proxy to match the shared proxy pod. Do NOT use proxy — the proxy pod's label is devbot-proxy, not proxy. A wrong label silently blocks all egress and the bot pod will hang waiting for the executor.
# Correct
- to:
- podSelector:
matchLabels:
app.kubernetes.io/name: devbot-proxy # ← must match the proxy pod label
ports:
- port: 3128
protocol: TCP
# ...
If using a separately deployed per-instance proxy, use its service name instead of devbot-proxy.
DNS Egress — Important¶
OpenShift uses custom DNS on port 5353 in the openshift-dns namespace, not the standard port 53. Your NetworkPolicy must allow:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: openshift-dns
ports:
- port: 5353
protocol: UDP
- port: 5353
protocol: TCP
Using port 53 or k8s-app: kube-dns will cause pods to hang — they can't resolve service names.
Reference PRs¶
- Add OpenShift deploy template — initial template
- Fix DNS egress and parameterize bot name — critical DNS fix
- Add BOT_JIRA_EMAIL env var — required for ticket assignment
- Add sprint prefix, Slack webhook — Slack notifications
- Fix proxy label in kessel NetworkPolicy — real-world example of wrong label causing bot hang
Step 2b: Scheduling (KEDA Cron Scaler)¶
Every instance must include a KEDA ScaledObject targeting its OpenShell SandboxWarmPool. Set BOT_REPLICAS to the off-hours baseline, normally 0; KEDA sets warm-pool replicas during configured windows.
The example template includes one cron trigger:
# --- Cron Scaler ---
- apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: ${BOT_NAME}-cron-scaler
labels:
app.kubernetes.io/name: ${BOT_NAME}
app.kubernetes.io/part-of: devbot
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: SandboxWarmPool
name: ${BOT_NAME}
minReplicaCount: 0
maxReplicaCount: 1
triggers:
- type: cron
metadata:
timezone: "Europe/Prague"
start: "0 9 * * 1-5"
end: "0 23 * * 1-5"
desiredReplicas: "1"
Adjust timezone, start, and end to match your team's working hours. The example above runs weekdays 9:00–23:00 Prague time.
For more schedule examples (US hours, weekends, split windows, etc.) and details on how multiple triggers combine, see the full Scheduling guide.
Your SaaS file's managedResourceTypes must include SandboxTemplate.agents.x-k8s.io, SandboxWarmPool.agents.x-k8s.io, and ScaledObject.keda.sh — see Step 4.
Step 3: Konflux CI/CD¶
This step must be completed before Step 4 (app-interface). App-interface references the Quay image built by Konflux — if the image doesn't exist or isn't public, the deployment will fail.
Follow the Konflux onboarding rules to register your repo. The key requirements:
- Onboard your repo to Konflux — follow the standard Konflux onboarding process for your tenant namespace
- Configure a ReleasePlan — your component needs a ReleasePlan and corresponding ReleasePlanAdmission so that push builds produce a release to your Quay prod repo
- Make the image public — configure the ReleasePlanAdmission to set the image repository as public. This is handled within the release config, not manually in Quay
Pipeline config:
# .tekton/my-bot-push.yaml (and pull-request.yaml)
dockerfile: dev-bot/Dockerfile.runner
path-context: .
Konflux auto-generates the .tekton/ pipeline files when you onboard. The important bits:
- Dockerfile path points to dev-bot/Dockerfile.runner (the submodule)
- Build context is . (the runner repo root)
- Push builds go to your Quay prod repo (via the ReleasePlan)
- PR builds go to quay.io/redhat-user-workloads/... with 5-day expiry
Reference PRs¶
- Konflux auto-registration — auto-generated pipeline files
Step 4: App-Interface Configuration¶
The namespace, app, and shared infrastructure (proxy, memory server, secrets) are already deployed by the primary instance. You do not need to do the full app-interface onboarding — just add your instance as a new deployment to the existing namespace.
Add to deploy.yml¶
Add your instance as a new resourceTemplate in the existing SaaS file:
resourceTemplates:
# ... existing primary instance ...
- name: my-bot-instance
path: /deploy/template.yaml
url: https://github.com/YourOrg/my-bot-instance
targets:
- namespace:
$ref: /services/insights/platform-frontend-ai-dev/namespaces/stage.hcmais01ue1.yml
ref: <git-commit-sha>
parameters:
BOT_IMAGE_DIGEST: sha256:<image-digest>
BOT_IMAGE: quay.io/your-org/my-bot-instance
BOT_NAME: devbot-myteam
BOT_LABEL: hcc-ai-myteam
BOT_REPLICAS: '0' # KEDA raises warm-pool replicas in window
BOT_BOARD_NAME: 'Your Board Name' # only used by claim-ticket for sprint assignment
BOT_SPRINT_PREFIX: 'Your Sprint' # only used by claim-ticket for sprint assignment
BOT_INCLUDE_BACKLOG: 'true'
BOT_INSTANCE_ID: 'Your Bot Name' # human-readable, used in memory server
GCP_PROJECT_ID: your-gcp-project
GCP_REGION: global
VERTEX_ALLOWED_MODELS: claude-sonnet-4-6,claude-opus-4-6,claude-haiku-4-5,claude-haiku-4-5@20251001
BOT_CONFIG_REPO: https://github.com/YourOrg/my-bot-instance.git
BOT_CONFIG_PATH: instance/my-config
# Optional, per deployment; requires the runner template to expose it:
# BOT_EXECUTION_ENGINE: coordinator
SLACK_WEBHOOK_URL: 'https://hooks.slack.com/...'
Opt one deployment into the coordinator¶
BOT_EXECUTION_ENGINE selects the runner engine for this deployment. It is not
an instance.yaml field, and an app-interface parameters entry does not
automatically become a container environment variable. In the instance repo's
runner deploy/template.yaml, declare a template parameter and forward it to
the runner container. Keep Python as the default:
Add this to the runner container's env list:
For a canary on only my-bot-instance, set this in that resource template's
targets[].parameters map:
Leave this unset (or python) for every other runner. Set it only when the
selected image contains the coordinator and its required configuration. For
an OpenCode canary, follow the OpenCode canary
runbook. This switch does not select
OpenCode V2: set runtime: opencode and a supported
provider in this instance's instance.yaml (or use the deployment fallbacks
when those keys are omitted).
Add managed resource types¶
Your SaaS file needs managedResourceTypes to include all resource kinds your template creates:
managedResourceTypes:
- SandboxTemplate.agents.x-k8s.io
- SandboxWarmPool.agents.x-k8s.io
- ScaledObject.keda.sh
- NetworkPolicy
Without these resource types, app-interface will prune the OpenShell resources or KEDA scaler on every sync.
Add image pattern¶
Add your Quay image to the imagePatterns list in deploy.yml:
Add to app.yml¶
Register your repo as a code component:
codeComponents:
- name: my-bot-instance
url: https://github.com/YourOrg/my-bot-instance
resource: upstream
Namespace¶
Your instance deploys to the same namespace as the primary instance — that's how it accesses the shared proxy, memory server, and secrets. No new namespace needed.
Vault Secrets¶
Every instance needs its own Vault secret with Jira credentials — a custom Jira identity means a custom proxy container. The memory server can still be shared.
Add a Vault secret to the namespace YAML:
# In namespaces/stage.hcmais01ue1.yml
openshiftResources:
- provider: vault-secret
path: your/vault/path/jira-credentials
name: myteam-jira-secrets
version: 1
annotations:
qontract.recycle: "true"
The secret needs two keys: jira-email and jira-token. Deploy a separate proxy template with JIRA_SECRET_NAME=myteam-jira-secrets so the Sandbox gets its own Jira identity.
The shared devbot-secrets secret (GitHub/GitLab/GPG/GCP credentials) is still used by all instances — only the Jira identity is per-instance.
devbot-secrets also carries an optional openai-api-key for the OpenAI-compatible
gateway on proxy port 8450. It is read only by the proxy container and is not required:
until the key exists, the gateway listener stays off and Claude via Vertex is unaffected.
See the prerequisites comment in deploy/template.yaml for the full key list.
Reference: Existing app-interface config¶
Use the framework instance (hcc-ui-agent-dev) as your reference — it always has the latest configuration. Its app-interface directory (data/services/insights/platform-frontend-ai-dev/) contains:
| File | Purpose |
|---|---|
app.yml |
App metadata, code components, service owners |
deploy.yml |
SaaS file — image patterns, resource templates, parameters |
namespaces/stage.hcmais01ue1.yml |
Namespace config, Vault secret ref, RDS definition |
pipelines/tekton-*.yml |
Tekton pipeline provider |
roles/ |
RBAC roles for namespace access |
Step 5: Jira Setup¶
- Create your label (e.g.
hcc-ai-myteam) in the Jira project - Label tickets the bot should pick up with your label +
repo:<name>labels - Board name (optional) — only used by
claim-ticketto add tickets to the active sprint. The ticket query itself is label-only.
repo: labels support both bare names (repo:my-frontend) and org-prefixed (repo:YourOrg/my-frontend). Both resolve against project-repos.json.
Step 6: Bot Fork Repos¶
For each repo in project-repos.json, the bot needs a fork to push branches to:
GitHub repos: - Create forks under a bot GitHub user/org - The bot pushes to the fork and opens PRs against the upstream
GitLab repos: - Create forks under a bot GitLab user/group - The bot pushes to the fork and opens MRs against the upstream
The shared devbot-secrets Vault secret provides GitHub (gh-bot-cli-token) and GitLab (gl-bot-cli-token) PATs. These tokens must have push access to your fork repos.
Verification¶
After deploying, verify in order:
- Warm pool starts:
oc get sandboxwarmpool devbot-myteamandoc get pods -l app.kubernetes.io/name=devbot-myteam - DNS works:
oc exec <pod> -- nslookup devbot-proxy— should resolve - Memory server reachable:
oc exec <pod> -- curl -s http://devbot-memory-server:8080/health - Executor reachable: check logs for "Connected to executor at devbot-proxy:9090"
- Config loaded: check logs for remote config sync from
BOT_CONFIG_REPO - Enable via schedule: verify KEDA raises
SandboxWarmPool.spec.replicasduring working hours and returns it to0outside the window.
Parameter Reference¶
| Parameter | Required | Description |
|---|---|---|
BOT_IMAGE |
yes | Quay image path |
BOT_IMAGE_DIGEST |
yes | Immutable image digest |
BOT_EXECUTION_ENGINE |
no | python by default; set to coordinator only for one opted-in runner. The runner template must declare and forward it; see Step 4. |
BOT_NAME |
yes | SandboxTemplate/SandboxWarmPool name (e.g. devbot-myteam) |
BOT_LABEL |
yes | Jira label to filter tickets |
BOT_REPLICAS |
no | Warm-pool baseline replicas; normally '0', KEDA-managed |
BOT_INSTANCE_ID |
yes | Human-readable name for memory server |
BOT_CONFIG_REPO |
yes | Git URL for remote config repo |
BOT_CONFIG_PATH |
yes | Path within config repo to agent/ dir |
GCP_PROJECT_ID |
yes | GCP project for Vertex AI |
GCP_REGION |
yes | GCP region (usually global) |
VERTEX_ALLOWED_MODELS |
yes | Comma-separated model allowlist |
BOT_BOARD_NAME |
no | Jira board name (for sprint assignment only) |
BOT_SPRINT_PREFIX |
no | Sprint name prefix filter (for sprint assignment only) |
BOT_INCLUDE_BACKLOG |
no | 'true' to include backlog tickets |
SLACK_WEBHOOK_URL |
no | Slack webhook — Incoming (/services/, recommended) or Workflow Builder (/triggers/) |
SLACK_NOTIFY_MODE |
no | immediate (default) or daily_digest. In digest mode, individual notifications are suppressed and a daily snapshot of open PRs is sent instead. Requires SLACK_DIGEST_HOUR to be set. |
SLACK_DIGEST_HOUR |
no | UTC hour (0-23) when daily digest is sent. Opt-in — digest is disabled unless this is set. |
PROXY_NAME |
no | Shared proxy service name (devbot-proxy) |
JIRA_SECRET_NAME |
no | Vault secret with jira-email + jira-token keys for separately deployed proxy |
Slack Workspace Configuration¶
Bot instances connect to a specific Slack workspace via their SLACK_USER_TOKEN. The token determines which workspace the bot operates in — production or sandbox.
Production vs Sandbox¶
| Production | Sandbox | |
|---|---|---|
| Workspace | redhat.enterprise.slack.com |
redhat-sandbox.enterprise.slack.com |
| Token | xoxp-... from production Vault |
xoxp-... from sandbox workspace |
| Channel IDs | Different per workspace | Different per workspace |
| Custom emoji | Workspace-specific | Must exist in sandbox too |
The hcc-framework-agent-dev instance connects to the production Slack grid. To test Slack features against the sandbox workspace, you need either:
- Option A: Separate dev instance — deploy a new instance with sandbox-specific Vault secrets (sandbox
SLACK_USER_TOKEN, sandbox channel IDs, sandbox emoji names). This is the cleanest approach. - Option B: Direct production testing — skip sandbox entirely and test in the existing dev instance against production Slack (requires Slack app approval for production workspace).
Slack Environment Variables¶
When pointing an instance at a different workspace, update all Slack-related parameters:
| Variable | Description |
|---|---|
SLACK_USER_TOKEN |
xoxp-... token for the target workspace |
SLACK_OPEN_PRS_CHANNEL |
Channel ID for the Open PRs thread (workspace-specific) |
SLACK_EMOJI_APPROVED |
Emoji name for approved PRs (e.g. lgtm-5363) |
SLACK_EMOJI_MERGED |
Emoji name for merged PRs (e.g. merged2) |
SLACK_WEBHOOK_URL |
Webhook URL (also workspace-specific) |
Channel IDs and custom emoji names differ between workspaces — you cannot reuse production values in sandbox.
Gotchas¶
NetworkPolicy proxy label must be devbot-proxy¶
The proxy pod's label is app.kubernetes.io/name: devbot-proxy — not proxy. Using the wrong label in your NetworkPolicy silently blocks all bot egress. The bot will start but hang forever waiting for the executor connection. This has caused real outages — see kessel-ai-dev#8.
DNS port is 5353, not 53¶
OpenShift uses a custom DNS server in the openshift-dns namespace on port 5353. Standard port 53 or kube-dns selectors won't work. Symptom: pods hang forever waiting for network connections.
GPG signing doesn't work for GitLab¶
Commits pushed to GitLab via the proxy are unsigned. GitHub commits are signed. This is a known limitation — GitLab requires a different GPG verification flow. A fix is in progress.
Submodule updates¶
When dev-bot merges new features, Konflux opens automated PRs to update the submodule in your runner repo. You can also update manually if you don't want to wait for the automation:
cd dev-bot && git pull origin master && cd ..
git add dev-bot && git commit -m "chore: update dev-bot submodule"
Merge order matters¶
When changes span multiple repos, merge in this order: 1. app-interface (config/params) — first, so the cluster config is ready 2. dev-bot (core changes) — second 3. runner instance (submodule bump) — last, after dev-bot merges
Shared identities¶
All bot instances share credentials from devbot-secrets — Jira, GitHub, GitLab, GPG, and GCP. This means all instances push code, open PRs, and comment on Jira as the same user. The bot identifies its own Jira comments by content patterns (structured reports, PR links, tables), not by username.
A different identity (Jira, GitHub, or GitLab) requires a separate proxy instance — credentials are baked into the proxy container, not the bot pod. See Shared vs Per-Instance Proxy in Step 2 and Vault Secrets in Step 4.
BOT_BOARD_NAME is fragile¶
If someone renames the Jira board, claim-ticket breaks. Consider using BOT_BOARD_ID (stable numeric ID) instead. The ticket query (new-work) doesn't use the board at all — it's label-only with sprint in openSprints().
Memory server is shared¶
All bot instances share one memory server. Task isolation is via instance_id — always pass it in task tool calls. Memories (learnings) are shared across instances, which is intentional.
Related Docs¶
- Presets overview — how all preset types work together
- Workflow presets — built-in workflow reference
- Env presets — available env presets for tools and runtimes
- Creating custom workflows — guide to building your own workflow
- Writing custom preflight scripts — pre-session data-gathering scripts
- Scheduling guide — KEDA cron scaler configuration
- Presets design doc — architecture decisions and rationale
- Roadmap — planned improvements