5.2 KiB
5.2 KiB
Hermes Automated Triage Demo — Runbook
How to arm, run, watch, and roll back the automated failure-to-repair demo.
Architecture background: knowledge/hermes-automated-triage-24h-plan.md.
What the loop does
You arm the failure (SEED_FAILURE=true)
-> Jenkins job hermes-triage-demo fails (fixture reads "unhealthy")
-> test-runner pod also writes the incident JSON to stdout
(Fluent Bit ships it to OpenSearch kube-*)
-> Ariadne polls the job every minute, opens incident <job>/<build>
-> Ariadne bundles Jenkins evidence + bounded OpenSearch excerpts
-> Ariadne calls the Hermes Agent API (/v1/runs) with
$triage-titan-test-failures
-> Hermes returns schema-valid diagnosis + requested_action
-> Ariadne authorizes (nine gates) and, if remediation is enabled,
creates Job hermes-demo-repair-<build> (writes "healthy")
-> Ariadne triggers ONE rebuild with SEED_FAILURE=false
-> rebuild passes -> incident resolved
Anything else -> human_required metric -> vmalert HermesTriageHumanRequired
Arming the demo
Jenkins UI: https://ci.bstein.dev/job/hermes-triage-demo/ → Build with
Parameters → check SEED_FAILURE → Build.
CLI (any Jenkins user API token):
curl -u <user>:<token> -X POST \
"https://ci.bstein.dev/job/hermes-triage-demo/buildWithParameters?SEED_FAILURE=true"
That is the only manual step. Everything after the red build is automatic.
Expected timings (measured live, first full loop 2026-08-05)
- Armed → seeded build red: 1m49s (mostly agent-pod scheduling; longer if the Jenkins agent capacity of 5 is busy with other CI builds).
- Red → repair Job succeeded: 1m15s (≤60 s detection tick + ~16 s Hermes diagnosis + authorization + repair pod).
- Repair → rebuild green: 32s.
- Green → incident resolved: 24s (next detection tick).
- Total armed-to-resolved: 4m00s. Budgeted Hermes timeout is 420 s; observed diagnosis runs were 15–21 s on gpt-5.6-terra. The only "silent" phases are the two Jenkins agent-pod waits.
Watching it live
- Jenkins:
https://ci.bstein.dev/job/hermes-triage-demo/(red build N, then green build N+1 withSEED_FAILURE=false). - Ariadne incident state:
GET http://ariadne.maintenance/api/internal/audit/events(in-cluster) or/api/admin/audit/events(Keycloak JWT) — event typeshermes_autotriage_incident,hermes_autotriage_diagnosis,hermes_autotriage_action. Status flow:detected → diagnosed → repairing → awaiting_rebuild → resolved. - Metrics (VictoriaMetrics / Grafana Explore):
ariadne_hermes_triage_incident{jenkins_job="hermes-triage-demo"},ariadne_hermes_triage_action_total,ariadne_hermes_triage_duration_seconds. - Repair evidence:
kubectl -n hermes-triage-demo get jobsshowshermes-demo-test-<N>(failed) andhermes-demo-repair-<N>(succeeded); both TTL-clean after 1 h. - Hermes side: the run appears in the dashboard at
https://agent.bstein.dev(session/run history). - Escalation path: alert
HermesTriageHumanRequiredin vmalert (vmalert-atlas-availabilitydeployment, 1 m interval,for: 2m) fires to Alertmanager for anyhuman_requiredincident. Gauges are republished every tick from stored incident state (restart-safe), and the alert self-clears once a newer build of the same job is green. Note: Alertmanager's default receiver is currently null — the alert is visible in vmalert/Alertmanager/Grafana but pushes no notification.
Demonstrating safe escalation (second path)
Any failure that does not match the demo-fixture signature — or any
invalid/low-confidence/unknown-action Hermes response — ends as
status="human_required" with no mutation. The simplest live demo:
temporarily set ARIADNE_HERMES_AUTOREMEDIATION_ENABLED=false (see below)
and arm the failure; Ariadne diagnoses fully but executes nothing, and the
alert fires instead.
Kill switch and rollback
- Instant behavioral off-switch (Flux-managed, in
services/maintenance/apps/ariadne-deployment.yaml):ARIADNE_HERMES_AUTOREMEDIATION_ENABLED=false→ diagnose-only.ARIADNE_HERMES_AUTOTRIAGE_ENABLED=false→ fully off. - The automatic loop can only ever: create Jobs named
hermes-demo-repair-*in namespacehermes-triage-demo, and trigger rebuilds of allowlisted jobs (ARIADNE_HERMES_AUTOTRIAGE_JOB_ALLOWLIST, currentlyhermes-triage-demoonly). One action + one rebuild per incident, ever (storage-backed idempotency). - Full teardown: remove
hermes-triage-demofromclusters/atlas/flux-system/applications/kustomization.yaml(Flux prunes the namespace) and delete thepipelineJob('hermes-triage-demo')block fromservices/jenkins/configmap-jcasc.yaml.
Credentials
- Ariadne → Hermes:
Authorization: Bearerkey shared via thehermes-api-server-keySecret present in bothhermesandmaintenancenamespaces (Hermes's init container seeds it into the persistent.env). NOTE: currently manually created (Vault migration pending — see plan handoff); rotating = write new value to both Secrets, restart hermes deployment and ariadne deployment. - Ariadne → Jenkins: existing
JENKINS_API_USER/TOKENfrom Vault (atlas/maintenance/ariadne-db).