Skip to content

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 to true if bot should only read, never push

mcp.json

Typically just points to the shared Jira MCP server:

{
  "mcpServers": {
    "mcp-atlassian": {
      "url": "${JIRA_MCP_URL}"
    }
  }
}

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


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_IMAGE and BOT_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


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:

  1. Onboard your repo to Konflux — follow the standard Konflux onboarding process for your tenant namespace
  2. Configure a ReleasePlan — your component needs a ReleasePlan and corresponding ReleasePlanAdmission so that push builds produce a release to your Quay prod repo
  3. 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


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:

parameters:
- name: BOT_EXECUTION_ENGINE
  value: python

Add this to the runner container's env list:

- name: BOT_EXECUTION_ENGINE
  value: ${BOT_EXECUTION_ENGINE}

For a canary on only my-bot-instance, set this in that resource template's targets[].parameters map:

BOT_EXECUTION_ENGINE: coordinator

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:

imagePatterns:
- quay.io/your-org/my-bot-instance

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

  1. Create your label (e.g. hcc-ai-myteam) in the Jira project
  2. Label tickets the bot should pick up with your label + repo:<name> labels
  3. Board name (optional) — only used by claim-ticket to 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:

  1. Warm pool starts: oc get sandboxwarmpool devbot-myteam and oc get pods -l app.kubernetes.io/name=devbot-myteam
  2. DNS works: oc exec <pod> -- nslookup devbot-proxy — should resolve
  3. Memory server reachable: oc exec <pod> -- curl -s http://devbot-memory-server:8080/health
  4. Executor reachable: check logs for "Connected to executor at devbot-proxy:9090"
  5. Config loaded: check logs for remote config sync from BOT_CONFIG_REPO
  6. Enable via schedule: verify KEDA raises SandboxWarmPool.spec.replicas during working hours and returns it to 0 outside 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"
Then bump the ref in app-interface after the image builds.

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.