titan-iac/services/maintenance/apps/ariadne-deployment.yaml
jenkins 100abc9dd5
Some checks failed
Tests / Declarative: Post Actions failed: 2, passed: 142
feat(ariadne): turn on suggested fixes for unpatchable findings
Most of the SonarQube backlog is refactors too diffuse for an anchored patch,
so without this the majority of what static analysis knows never reaches a
maintainer. Deduped on the rule like the pull requests, so one root cause
yields one issue.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 05:00:25 -03:00

675 lines
31 KiB
YAML

# services/maintenance/apps/ariadne-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ariadne
namespace: maintenance
spec:
replicas: 1
revisionHistoryLimit: 3
selector:
matchLabels:
app: ariadne
template:
metadata:
labels:
app: ariadne
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"
maintenance.bstein.dev/restart-rev: "20260413-jenkins-api-2"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "maintenance"
vault.hashicorp.com/agent-requests-cpu: "25m"
vault.hashicorp.com/agent-limits-cpu: "100m"
vault.hashicorp.com/agent-requests-mem: "32Mi"
vault.hashicorp.com/agent-limits-mem: "128Mi"
vault.hashicorp.com/agent-inject-secret-ariadne-env.sh: "kv/data/atlas/maintenance/ariadne-db"
vault.hashicorp.com/agent-inject-template-ariadne-env.sh: |
{{ with secret "kv/data/atlas/maintenance/ariadne-db" }}
export ARIADNE_DATABASE_URL="{{ .Data.data.database_url }}"
export JENKINS_API_USER="{{ .Data.data.jenkins_api_user }}"
export JENKINS_API_TOKEN="{{ .Data.data.jenkins_api_token }}"
{{- if .Data.data.sonarqube_token }}
export ARIADNE_HERMES_SONAR_TOKEN="{{ .Data.data.sonarqube_token }}"
{{- end }}
{{- if .Data.data.game_mode_hook_token }}
export GAME_MODE_HOOK_TOKEN="{{ .Data.data.game_mode_hook_token }}"
{{- end }}
{{ end }}
{{ with secret "kv/data/atlas/portal/atlas-portal-db" }}
export PORTAL_DATABASE_URL="{{ .Data.data.PORTAL_DATABASE_URL }}"
{{ end }}
{{ with secret "kv/data/atlas/portal/bstein-dev-home-keycloak-admin" }}
export KEYCLOAK_ADMIN_CLIENT_SECRET="{{ .Data.data.client_secret }}"
{{ end }}
{{ with secret "kv/data/atlas/nextcloud/nextcloud-db" }}
export NEXTCLOUD_DB_NAME="{{ .Data.data.database }}"
export NEXTCLOUD_DB_USER="{{ index .Data.data "db-username" }}"
export NEXTCLOUD_DB_PASSWORD="{{ index .Data.data "db-password" }}"
{{ end }}
{{ with secret "kv/data/atlas/nextcloud/nextcloud-admin" }}
export NEXTCLOUD_ADMIN_USER="{{ index .Data.data "admin-user" }}"
export NEXTCLOUD_ADMIN_PASSWORD="{{ index .Data.data "admin-password" }}"
{{ end }}
{{ with secret "kv/data/atlas/health/wger-admin" }}
export WGER_ADMIN_USERNAME="{{ .Data.data.username }}"
export WGER_ADMIN_PASSWORD="{{ .Data.data.password }}"
{{ end }}
{{ with secret "kv/data/atlas/finance/firefly-secrets" }}
export FIREFLY_CRON_TOKEN="{{ .Data.data.STATIC_CRON_TOKEN }}"
{{ end }}
{{ with secret "kv/data/atlas/mailu/mailu-db-secret" }}
export MAILU_DB_NAME="{{ .Data.data.database }}"
export MAILU_DB_USER="{{ .Data.data.username }}"
export MAILU_DB_PASSWORD="{{ .Data.data.password }}"
{{ end }}
{{ with secret "kv/data/atlas/mailu/mailu-initial-account-secret" }}
export SMTP_HOST="mailu-front.mailu-mailserver.svc.cluster.local"
export SMTP_PORT="587"
export SMTP_STARTTLS="true"
export SMTP_USE_TLS="false"
export SMTP_USERNAME="no-reply-portal@bstein.dev"
export SMTP_PASSWORD="{{ .Data.data.password }}"
export SMTP_FROM="no-reply-portal@bstein.dev"
export MAILU_SYSTEM_PASSWORD="{{ .Data.data.password }}"
{{ end }}
{{ with secret "kv/data/atlas/comms/mas-admin-client-runtime" }}
export COMMS_MAS_ADMIN_CLIENT_SECRET="{{ .Data.data.client_secret }}"
{{ end }}
{{ with secret "kv/data/atlas/comms/atlasbot-credentials-runtime" }}
export COMMS_BOT_PASSWORD="{{ index .Data.data "bot-password" }}"
export COMMS_SEEDER_PASSWORD="{{ index .Data.data "seeder-password" }}"
{{ end }}
{{ with secret "kv/data/atlas/comms/synapse-admin" }}
export COMMS_SYNAPSE_ADMIN_TOKEN="{{ .Data.data.access_token }}"
{{ end }}
{{ with secret "kv/data/atlas/comms/synapse-db" }}
export COMMS_SYNAPSE_DB_PASSWORD="{{ .Data.data.POSTGRES_PASSWORD }}"
{{ end }}
{{ with secret "kv/data/atlas/vault/vault-oidc-config" }}
export VAULT_OIDC_DISCOVERY_URL="{{ .Data.data.discovery_url }}"
export VAULT_OIDC_CLIENT_ID="{{ .Data.data.client_id }}"
export VAULT_OIDC_CLIENT_SECRET="{{ .Data.data.client_secret }}"
export VAULT_OIDC_DEFAULT_ROLE="{{ .Data.data.default_role }}"
export VAULT_OIDC_SCOPES="{{ .Data.data.scopes }}"
export VAULT_OIDC_USER_CLAIM="{{ .Data.data.user_claim }}"
export VAULT_OIDC_GROUPS_CLAIM="{{ .Data.data.groups_claim }}"
{{- if .Data.data.token_policies }}
export VAULT_OIDC_TOKEN_POLICIES="{{ .Data.data.token_policies }}"
{{- else }}
export VAULT_OIDC_TOKEN_POLICIES="default"
{{- end }}
export VAULT_OIDC_ADMIN_GROUP="{{ .Data.data.admin_group }}"
export VAULT_OIDC_ADMIN_POLICIES="{{ .Data.data.admin_policies }}"
{{- if .Data.data.admin_bound_claims }}
export VAULT_OIDC_ADMIN_BOUND_CLAIMS="{{ .Data.data.admin_bound_claims }}"
{{- else }}
export VAULT_OIDC_ADMIN_BOUND_CLAIMS="{\"groups\":\"admin\"}"
{{- end }}
export VAULT_OIDC_DEV_GROUP="{{ .Data.data.dev_group }}"
export VAULT_OIDC_DEV_POLICIES="{{ .Data.data.dev_policies }}"
export VAULT_OIDC_USER_GROUP="{{ .Data.data.user_group }}"
export VAULT_OIDC_USER_POLICIES="{{ .Data.data.user_policies }}"
export VAULT_OIDC_REDIRECT_URIS="{{ .Data.data.redirect_uris }}"
export VAULT_OIDC_BOUND_AUDIENCES="{{ .Data.data.bound_audiences }}"
{{- if .Data.data.ui_default_auth_method }}
export VAULT_UI_DEFAULT_AUTH_METHOD="{{ .Data.data.ui_default_auth_method }}"
{{- else }}
export VAULT_UI_DEFAULT_AUTH_METHOD="oidc"
{{- end }}
{{- if .Data.data.ui_default_auth_path }}
export VAULT_UI_DEFAULT_AUTH_PATH="{{ .Data.data.ui_default_auth_path }}"
{{- else }}
export VAULT_UI_DEFAULT_AUTH_PATH="oidc"
{{- end }}
{{- if .Data.data.bound_claims_type }}
export VAULT_OIDC_BOUND_CLAIMS_TYPE="{{ .Data.data.bound_claims_type }}"
{{- else }}
export VAULT_OIDC_BOUND_CLAIMS_TYPE="string"
{{- end }}
{{ end }}
spec:
serviceAccountName: ariadne
nodeSelector:
kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: "true"
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: hardware
operator: In
values: ["rpi5"]
- weight: 95
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- titan-13
- titan-14
- titan-15
- titan-17
- titan-19
- weight: 80
preference:
matchExpressions:
- key: atlas.bstein.dev/spillover
operator: DoesNotExist
- weight: 50
preference:
matchExpressions:
- key: hardware
operator: In
values: ["rpi4"]
containers:
- name: ariadne
image: registry.bstein.dev/bstein/ariadne:latest
imagePullPolicy: Always
command: ["/bin/sh", "-c"]
args:
- >-
. /vault/secrets/ariadne-env.sh
&& exec uvicorn ariadne.app:app --host 0.0.0.0 --port 8080
ports:
- name: http
containerPort: 8080
env:
- name: KEYCLOAK_URL
value: https://sso.bstein.dev
- name: KEYCLOAK_REALM
value: atlas
- name: KEYCLOAK_CLIENT_ID
value: bstein-dev-home
- name: KEYCLOAK_EXTRA_CLIENT_IDS
value: wolf
- name: KEYCLOAK_ISSUER
value: https://sso.bstein.dev/realms/atlas
- name: KEYCLOAK_JWKS_URL
value: http://keycloak.sso.svc.cluster.local/realms/atlas/protocol/openid-connect/certs
- name: KEYCLOAK_ADMIN_URL
value: http://keycloak.sso.svc.cluster.local
- name: KEYCLOAK_ADMIN_REALM
value: atlas
- name: KEYCLOAK_ADMIN_CLIENT_ID
value: bstein-dev-home-admin
- name: PORTAL_PUBLIC_BASE_URL
value: https://bstein.dev
- name: ARIADNE_LOG_LEVEL
value: INFO
- name: ARIADNE_DB_POOL_MIN
value: "0"
- name: ARIADNE_DB_POOL_MAX
value: "5"
- name: ARIADNE_DB_CONNECT_TIMEOUT_SEC
value: "5"
- name: ARIADNE_DB_LOCK_TIMEOUT_SEC
value: "5"
- name: ARIADNE_DB_STATEMENT_TIMEOUT_SEC
value: "30"
- name: ARIADNE_DB_IDLE_IN_TX_TIMEOUT_SEC
value: "10"
- name: ARIADNE_RUN_MIGRATIONS
value: "false"
- name: PORTAL_ADMIN_USERS
value: bstein
- name: PORTAL_ADMIN_GROUPS
value: admin
- name: ACCOUNT_ALLOWED_GROUPS
value: dev,admin
- name: ALLOWED_FLAG_GROUPS
value: demo,test,vaultwarden_grandfathered
- name: DEFAULT_USER_GROUPS
value: dev
- name: MAILU_DOMAIN
value: bstein.dev
- name: MAILU_HOST
value: mail.bstein.dev
- name: MAILU_SYNC_URL
value: http://ariadne.maintenance.svc.cluster.local/events
- name: MAILU_EVENT_MIN_INTERVAL_SEC
value: "10"
- name: MAILU_SYSTEM_USERS
value: no-reply-portal@bstein.dev,no-reply-vaultwarden@bstein.dev
- name: MAILU_MAILBOX_WAIT_TIMEOUT_SEC
value: "180"
- name: MAILU_DB_HOST
value: postgres-service.postgres.svc.cluster.local
- name: MAILU_DB_PORT
value: "5432"
- name: NEXTCLOUD_NAMESPACE
value: nextcloud
- name: NEXTCLOUD_POD_LABEL
value: app=nextcloud
- name: NEXTCLOUD_CONTAINER
value: nextcloud
- name: NEXTCLOUD_EXEC_TIMEOUT_SEC
value: "120"
- name: NEXTCLOUD_URL
value: https://cloud.bstein.dev
- name: NEXTCLOUD_DB_HOST
value: postgres-service.postgres.svc.cluster.local
- name: NEXTCLOUD_DB_PORT
value: "5432"
- name: WGER_NAMESPACE
value: health
- name: WGER_USER_SYNC_WAIT_TIMEOUT_SEC
value: "90"
- name: WGER_POD_LABEL
value: app=wger
- name: WGER_CONTAINER
value: wger
- name: WGER_ADMIN_EMAIL
value: brad@bstein.dev
- name: FIREFLY_NAMESPACE
value: finance
- name: FIREFLY_USER_SYNC_WAIT_TIMEOUT_SEC
value: "90"
- name: FIREFLY_POD_LABEL
value: app=firefly
- name: FIREFLY_CONTAINER
value: firefly
- name: FIREFLY_CRON_BASE_URL
value: http://firefly.finance.svc.cluster.local/api/v1/cron
- name: FIREFLY_CRON_TIMEOUT_SEC
value: "30"
- name: VAULT_NAMESPACE
value: vault
- name: VAULT_ADDR
value: http://vault.vault.svc.cluster.local:8200
- name: VAULT_K8S_ROLE
value: vault-admin
- name: VAULT_K8S_ROLE_TTL
value: 1h
- name: GAME_MODE_NODE_NAME
value: titan-24
- name: GAME_MODE_DISPLACE_WORKLOADS
value: "[]"
- name: GAME_MODE_LEASE_NAMESPACE
value: hermes
- name: GAME_MODE_LEASE_NAME
value: titan-24-gpu-owner
- name: GAME_MODE_OLLAMA_URL
value: http://hermes-ollama.hermes.svc.cluster.local:11434
- name: GAME_MODE_OLLAMA_MODEL
value: gpt-oss:20b
- name: GAME_MODE_OLLAMA_REQUEST_TIMEOUT_SEC
value: "900"
- name: GAME_MODE_TRANSITION_TIMEOUT_SEC
value: "900"
- name: GAME_MODE_POLL_INTERVAL_SEC
value: "1"
- name: WOLF_OIDC_CLIENT_ID
value: wolf
- name: WOLF_OIDC_BASE_URL
value: https://wolf.bstein.dev
- name: WOLF_OIDC_VAULT_PATH
value: game-stream/wolf-oidc
- name: ARIADNE_SCHEDULE_WOLF_OIDC
value: "17 */6 * * *"
- name: WOLF_API_URL
value: http://wolf-api.game-stream.svc.cluster.local:8088
- name: WOLF_GATEKEEPER_URL
value: http://wolf-gatekeeper.game-stream.svc.cluster.local:8087
- name: GAME_STREAM_FIREWALL_UNLOCK_TTL_SEC
value: "28800"
- name: GAME_STREAM_MOONLIGHT_HOST
value: moonlight.bstein.dev
- name: GAME_STREAM_USER_GROUP
value: game-stream-users
- name: GAME_STREAM_ADMIN_GROUP
value: admin
- name: GAME_STREAM_PROFILE_GROUP_PREFIX
value: game-stream-profile-
- name: COMMS_NAMESPACE
value: comms
- name: COMMS_SYNAPSE_BASE
value: http://othrys-synapse-matrix-synapse.comms.svc.cluster.local:8008
- name: COMMS_AUTH_BASE
value: http://matrix-authentication-service.comms.svc.cluster.local:8080
- name: COMMS_MAS_ADMIN_API_BASE
value: http://matrix-authentication-service.comms.svc.cluster.local:8081/api/admin/v1
- name: COMMS_MAS_TOKEN_URL
value: http://matrix-authentication-service.comms.svc.cluster.local:8080/oauth2/token
- name: COMMS_MAS_ADMIN_CLIENT_ID
value: 01KDXMVQBQ5JNY6SEJPZW6Z8BM
- name: COMMS_SERVER_NAME
value: live.bstein.dev
- name: COMMS_ROOM_ALIAS
value: "#othrys:live.bstein.dev"
- name: COMMS_ROOM_NAME
value: Othrys
- name: COMMS_PIN_MESSAGE
value: "Invite guests: share https://live.bstein.dev/#/room/#othrys:live.bstein.dev?action=join and choose 'Continue' -> 'Join as guest'."
- name: COMMS_SEEDER_USER
value: othrys-seeder
- name: COMMS_BOT_USER
value: atlasbot
- name: COMMS_SYNAPSE_DB_HOST
value: postgres-service.postgres.svc.cluster.local
- name: COMMS_SYNAPSE_DB_PORT
value: "5432"
- name: COMMS_SYNAPSE_DB_NAME
value: synapse
- name: COMMS_SYNAPSE_DB_USER
value: synapse
- name: COMMS_TIMEOUT_SEC
value: "30"
- name: COMMS_GUEST_STALE_DAYS
value: "14"
- name: VAULTWARDEN_NAMESPACE
value: vaultwarden
- name: VAULTWARDEN_POD_LABEL
value: app=vaultwarden
- name: VAULTWARDEN_POD_PORT
value: "80"
- name: VAULTWARDEN_SERVICE_HOST
value: vaultwarden-service.vaultwarden.svc.cluster.local
- name: VAULTWARDEN_ADMIN_SECRET_NAME
value: vaultwarden-admin
- name: VAULTWARDEN_ADMIN_SECRET_KEY
value: ADMIN_TOKEN
- name: VAULTWARDEN_ADMIN_SESSION_TTL_SEC
value: "900"
- name: VAULTWARDEN_ADMIN_RATE_LIMIT_BACKOFF_SEC
value: "600"
- name: VAULTWARDEN_RETRY_COOLDOWN_SEC
value: "1800"
- name: VAULTWARDEN_FAILURE_BAILOUT
value: "2"
- name: ARIADNE_PROVISION_POLL_INTERVAL_SEC
value: "5"
- name: ARIADNE_PROVISION_RETRY_COOLDOWN_SEC
value: "30"
- name: ARIADNE_SCHEDULE_TICK_SEC
value: "5"
- name: ARIADNE_SCHEDULE_MAILU_SYNC
value: "30 4 * * *"
- name: ARIADNE_SCHEDULE_NEXTCLOUD_SYNC
value: "*/15 * * * *"
- name: ARIADNE_SCHEDULE_NEXTCLOUD_CRON
value: "*/5 * * * *"
- name: ARIADNE_SCHEDULE_NEXTCLOUD_MAINTENANCE
value: "30 4 * * *"
- name: ARIADNE_SCHEDULE_VAULTWARDEN_SYNC
value: "*/15 * * * *"
- name: ARIADNE_SCHEDULE_WGER_USER_SYNC
value: "*/15 * * * *"
- name: ARIADNE_SCHEDULE_WGER_ADMIN
value: "15 3 * * *"
- name: ARIADNE_SCHEDULE_FIREFLY_USER_SYNC
value: "*/15 * * * *"
- name: ARIADNE_SCHEDULE_FIREFLY_CRON
value: "0 3 * * *"
- name: ARIADNE_SCHEDULE_POD_CLEANER
value: "*/30 * * * *"
- name: ARIADNE_SCHEDULE_OPENSEARCH_PRUNE
value: "23 3 * * *"
- name: ARIADNE_SCHEDULE_IMAGE_SWEEPER
value: "0 */4 * * *"
- name: ARIADNE_SCHEDULE_VAULT_K8S_AUTH
value: "0 0 1 1 *"
- name: ARIADNE_SCHEDULE_VAULT_OIDC
value: "0 0 1 1 *"
- name: ARIADNE_SCHEDULE_COMMS_GUEST_NAME
value: "*/5 * * * *"
- name: ARIADNE_SCHEDULE_COMMS_PIN_INVITE
value: "0 0 1 * *"
- name: ARIADNE_SCHEDULE_COMMS_RESET_ROOM
value: "0 0 1 1 *"
- name: ARIADNE_SCHEDULE_COMMS_SEED_ROOM
value: "*/10 * * * *"
- name: ARIADNE_SCHEDULE_CLUSTER_STATE
value: "*/10 * * * *"
- name: ARIADNE_CLUSTER_STATE_KEEP
value: "720"
- name: WELCOME_EMAIL_ENABLED
value: "true"
- name: K8S_API_TIMEOUT_SEC
value: "5"
- name: ARIADNE_VM_URL
value: http://victoria-metrics-single-server.monitoring.svc.cluster.local:8428
- name: ARIADNE_CLUSTER_STATE_VM_TIMEOUT_SEC
value: "30"
- name: ARIADNE_ALERTMANAGER_URL
value: http://alertmanager.monitoring.svc.cluster.local
- name: OPENSEARCH_URL
value: http://opensearch-master.logging.svc.cluster.local:9200
- name: OPENSEARCH_LIMIT_BYTES
value: "1099511627776"
- name: OPENSEARCH_INDEX_PATTERNS
value: kube-*,journald-*,trace-analytics-*
- name: METIS_BASE_URL
value: http://metis.maintenance.svc.cluster.local
- name: METIS_TIMEOUT_SEC
value: "15"
- name: ARIADNE_SCHEDULE_METIS_SENTINEL_WATCH
value: "*/30 * * * *"
- name: ARIADNE_SCHEDULE_JENKINS_BUILD_WEATHER
value: "*/10 * * * *"
- name: JENKINS_BASE_URL
value: https://ci.bstein.dev
- name: JENKINS_API_TIMEOUT_SEC
value: "10"
- name: ARIADNE_SCHEDULE_JENKINS_WORKSPACE_CLEANUP
value: "45 */6 * * *"
- name: ARIADNE_SCHEDULE_TESTING_TRIAGE
value: "*/15 * * * *"
- name: ARIADNE_SCHEDULE_HERMES_AUTOTRIAGE
value: "* * * * *"
- name: ARIADNE_HERMES_AUTOTRIAGE_ENABLED
value: "true"
- name: ARIADNE_HERMES_AUTOTRIAGE_JOB_ALLOWLIST
value: hermes-triage-demo,hermes-code-demo,ariadne,lesavka,soteria,metis,ananke,pegasus,atlasbot,bstein-dev-home,data-prepper
- name: ARIADNE_HERMES_AUTOREMEDIATION_ENABLED
value: "true"
- name: ARIADNE_HERMES_ALLOWED_ACTIONS
value: repair_demo_fixture,retry_transient_infra,reclaim_workspace_storage,clear_stuck_agent_pods
# Each classification maps to exactly one action id. A diagnosis
# whose classification is absent here can never reach an action,
# and a diagnosis asking for an action that is not its
# classification's own is refused before anything runs.
- name: ARIADNE_HERMES_ACTION_CLASSIFICATIONS
value: known_demo_fixture_failure=repair_demo_fixture,transient_infra_failure=retry_transient_infra,workspace_storage_exhausted=reclaim_workspace_storage,jenkins_agent_provisioning_failure=clear_stuck_agent_pods
# Adds a service's own namespace to log evidence alongside
# jenkins, so a build failure that coincides with the service being
# unhealthy carries some trace of the service. Only mapped jobs are
# affected; of the triaged services only this one runs in-cluster.
- name: ARIADNE_HERMES_JOB_NAMESPACES
value: bstein-dev-home=bstein-dev-home
- name: ARIADNE_HERMES_MIN_CONFIDENCE
value: "0.85"
- name: ARIADNE_HERMES_MAX_ACTIONS_PER_INCIDENT
value: "1"
- name: ARIADNE_HERMES_API_URL
value: http://hermes.hermes.svc.cluster.local:8642
- name: ARIADNE_HERMES_RUN_TIMEOUT_SECONDS
value: "420"
- name: ARIADNE_HERMES_API_KEY
valueFrom:
secretKeyRef:
name: hermes-api-server-key
key: api-key
optional: true
- name: ARIADNE_HERMES_CODE_ENABLED
value: "true"
- name: ARIADNE_HERMES_CODE_JOB
value: hermes-code-demo
- name: ARIADNE_HERMES_CODE_OWNER
value: bstein
- name: ARIADNE_HERMES_CODE_REPO
value: hermes-code-demo
- name: ARIADNE_HERMES_CODE_BASE_BRANCH
value: master
- name: ARIADNE_HERMES_CODE_CANDIDATE_PATH
value: src/discount.py
- name: ARIADNE_HERMES_CODE_ALLOWED_PREFIXES
value: src/
- name: ARIADNE_HERMES_CODE_ALLOWED_SUFFIXES
value: .py
- name: ARIADNE_HERMES_CODE_MAX_PATCH_BYTES
value: "4000"
- name: ARIADNE_HERMES_CODE_MAX_CHANGED_LINES
value: "20"
- name: ARIADNE_HERMES_GITEA_BASE_URL
value: https://scm.bstein.dev
# File a Gitea issue in the failing service's own repository when
# triage concludes a human is needed. Opt-in per job: only jobs
# mapped here can ever receive an issue. Deduplicated by
# job+classification so a repeatedly failing job produces one
# issue per kind of failure, not one per build.
- name: ARIADNE_HERMES_ISSUES_ENABLED
value: "true"
- name: ARIADNE_HERMES_ISSUE_REPOS
value: metis=bstein/metis,lesavka=bstein/lesavka,soteria=bstein/soteria,bstein-dev-home=bstein/bstein-dev-home,ariadne=bstein/ariadne,ananke=bstein/ananke,pegasus=bstein/pegasus,atlasbot=bstein/atlasbot
- name: ARIADNE_HERMES_ISSUE_DEDUPE_SCOPE
value: classification
- name: ARIADNE_HERMES_ISSUE_MAX_PER_TICK
value: "2"
# Homegrown repositories that may receive a Hermes patch proposal.
# Escalated incidents on these jobs get an issue AND, when the
# evidence implicates source files, a pull request on a
# hermes-repair/<build> branch. Path allowlists are per repo so a
# proposal can only ever touch that repo's source tree.
# Review capacity, not correctness: proposals are cheap to make
# and expensive to read. Below this ceiling an open pull request on
# a repository is no reason to withhold a fix for a different
# failure; at it, new proposals wait.
# What Hermes may be asked to propose a fix for. Deliberately not
# in ARIADNE_HERMES_ALLOWED_ACTIONS: that list gates what Ariadne
# executes on its own authority, whereas everything here becomes a
# pull request a person reads before anything changes.
- name: ARIADNE_HERMES_FIX_CATEGORIES
value: sonarqube_issue,lint_violation,undefined_name,failing_assertion
- name: ARIADNE_HERMES_CODE_MAX_OPEN_PROPOSALS
value: "64"
# The SonarQube sweep: the second way into the proposal flow. Triage
# enters on a build failure; static analysis never fails anything, so
# its findings would otherwise sit unread forever. From evidence
# collection onward the path is identical, gates included.
# Printed in every pull request Hermes proposes, so a reviewer can
# open the run that wrote it rather than taking "Proposed by
# Hermes" on trust.
- name: ARIADNE_HERMES_UI_URL
value: https://agent.bstein.dev
- name: ARIADNE_HERMES_SONAR_ENABLED
value: "true"
- name: ARIADNE_HERMES_SONAR_URL
value: http://sonarqube.quality.svc.cluster.local:9000
# The browsable console, printed in each proposal so the finding
# that caused it is one click away.
- name: ARIADNE_HERMES_SONAR_UI_URL
value: https://quality.bstein.dev
# sonar-project=jenkins-job. The job already carries the repository
# mapping in ARIADNE_HERMES_CODE_REPOS, so there is no second place
# for the two to disagree. Only projects whose job also has a write
# allowlist in ARIADNE_HERMES_CODE_PREFIXES are listed: without one
# nothing is patchable, so a sweep would spend a SonarQube call to
# discover it has nowhere to write. That excludes ananke, pegasus,
# atlasbot, data_prepper and titan_iac for now.
- name: ARIADNE_HERMES_SONAR_PROJECTS
value: ariadne=ariadne,metis=metis,soteria=soteria,bstein_home=bstein-dev-home,ananke=ananke,pegasus=pegasus,atlasbot=atlasbot
# Hotspots are absent by construction - they need human review, not a
# patch - so only findings with a code fix are ever requested.
- name: ARIADNE_HERMES_SONAR_TYPES
value: CODE_SMELL,BUG
# One per project per hourly sweep across seven projects. Rule-level
# dedupe means a project stops producing proposals once every rule
# it has is already under review, so this is a ceiling rather than a
# rate: the backlog cannot turn into a pull request queue nobody
# drains. The 64-open-proposal ceiling still applies on top.
# Findings with no possible patch become an issue carrying the fix
# Hermes would make. Most of the backlog is refactors too diffuse
# for an anchored patch, so without this the majority of what
# SonarQube knows never reaches a maintainer at all. Deduped on the
# rule, exactly like the pull requests.
- name: ARIADNE_HERMES_SONAR_ADVICE_ENABLED
value: "true"
- name: ARIADNE_HERMES_SONAR_MAX_PER_SWEEP
value: "7"
# SonarQube's own effort estimate is the filter, not severity: it is
# the closest proxy for the single anchored change the patch
# validator can check. Anything it calls longer than this is a
# refactor for a person.
- name: ARIADNE_HERMES_SONAR_MAX_EFFORT_MINUTES
value: "20"
- name: ARIADNE_HERMES_CODE_REPOS
value: metis=bstein/metis,lesavka=bstein/lesavka,soteria=bstein/soteria,bstein-dev-home=bstein/bstein-dev-home,ariadne=bstein/ariadne,ananke=bstein/ananke,pegasus=bstein/pegasus,atlasbot=bstein/atlasbot
- name: ARIADNE_HERMES_CODE_BASE_BRANCHES
value: metis=master,lesavka=master,soteria=main,bstein-dev-home=master,ariadne=master,ananke=main,pegasus=main,atlasbot=main
- name: ARIADNE_HERMES_CODE_PREFIXES
value: metis=cmd/|pkg/,lesavka=server/|client/|common/,soteria=cmd/|internal/,bstein-dev-home=backend/|frontend/,ariadne=ariadne/,ananke=cmd/|internal/,pegasus=backend/|frontend/,atlasbot=atlasbot/
- name: ARIADNE_HERMES_CODE_SUFFIXES
value: metis=.go,lesavka=.rs,soteria=.go,bstein-dev-home=.py|.ts|.tsx|.js,ariadne=.py,ananke=.go,pegasus=.go|.ts|.tsx|.js,atlasbot=.py
- name: ARIADNE_HERMES_GITEA_TOKEN
valueFrom:
secretKeyRef:
name: hermes-gitea-token
key: token
optional: true
- name: ARIADNE_TESTING_TRIAGE_MODEL_URL
value: http://hermes-model-gate.hermes.svc.cluster.local:11434
- name: ARIADNE_TESTING_TRIAGE_MODEL
value: gpt-oss:20b
- name: ARIADNE_TESTING_TRIAGE_MODEL_TIMEOUT_SEC
value: "900"
- name: JENKINS_WORKSPACE_NAMESPACE
value: jenkins
- name: JENKINS_WORKSPACE_PVC_PREFIX
value: pvc-workspace-
- name: JENKINS_WORKSPACE_CLEANUP_MIN_AGE_HOURS
value: "24"
- name: JENKINS_WORKSPACE_CLEANUP_DRY_RUN
value: "false"
- name: JENKINS_WORKSPACE_CLEANUP_MAX_DELETIONS_PER_RUN
value: "20"
- name: METRICS_PATH
value: "/metrics"
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
# timeoutSeconds defaults to 1, which this pod cannot honour. The
# auto-triage tick runs every minute and spends most of it waiting on
# Jenkins, OpenSearch, Gitea and Hermes; against a 500m CPU limit the
# event loop occasionally takes longer than a second to answer
# /health. Three of those in a row and the kubelet kills a container
# that is working perfectly well. Observed 11 times in 139 minutes,
# each one dropping triage ticks for the length of a restart.
# Startup is the other way this pod gets killed while healthy. It
# runs migrations and builds its schedule before it listens, which
# can take longer than liveness allows from initialDelaySeconds
# alone, and the kubelet then reports "connection refused" and
# restarts a pod that was simply still booting. A startupProbe gives
# boot up to five minutes and only then hands over to liveness.
startupProbe:
httpGet:
path: /health
port: http
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 30
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 5
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3