Prometheus, Mimir, and Grafana — for agents
Target audience: an AI agent (or a human working through one) that has to do things with metrics — query, triage, alert, build and fix dashboards, and pick the right KPIs — against a stack that runs Prometheus, Grafana Mimir, and/or Grafana. Works whether the agent is given curl access to a Mimir gateway, an MCP server wrapper, or just a Grafana URL and a service-account token.
This skill uses the stack; it does not change it. Siblings in the
observability plugin own the lifecycle: version-laddering a Mimir install is
mimir-upgrade, getting SNMP devices to produce metrics in the first place
is snmp-exporter, and logs — a different pipeline entirely — are
logging-operator (with rancher-logging-exit for migrating off the
Rancher-bundled chart). If a query returns nothing, check here first; if the
component itself is broken or stale, you are in the wrong skill.
Why this matters
Metrics lie in three directions: (1) the agent queries the wrong metric or wrong label, (2) the query is syntactically fine but semantically broken (rate after aggregation, histogram_quantile of the mean, default histogram buckets sized for the wrong service), (3) the dashboard looks correct but the datasource variable is empty or the unit is off by 1000×. Each failure mode has an easy check. This skill is those checks, organized so the agent reaches for them before issuing the first query.
The one-paragraph rubric
Prometheus stores samples identified by metric_name{label=value, ...}. Mimir is a horizontally-scalable multi-tenant store that speaks Prometheus's wire protocol and API under a /prometheus/api/v1/… prefix, gated by X-Scope-OrgID. Grafana is the UI and the dashboards-as-JSON store. PromQL returns instant vectors, range vectors, scalars, or strings; rate() always wraps a counter before any sum; histogram_quantile() always consumes aggregated _bucket rates; and $__rate_interval is the only interval variable safe for counter rates. RED answers is my service OK, USE answers is my resource OK, and Golden Signals + SLO burn-rate answer are my users OK. Everything else is detail in the references.
Connect first — figure out what endpoint is in front of the agent
Before the first query, identify the endpoint and auth shape. One of these five patterns will match:
| Shape |
Base URL |
Auth |
Tenant |
| Prometheus direct |
http://prometheus:9090 |
usually none / optional Basic |
n/a |
| Self-hosted Mimir via gateway |
https://mimir.example.com/prometheus |
Authorization: Bearer … |
X-Scope-OrgID: <tenant> |
| Grafana Cloud Mimir |
https://prometheus-prod-XX.grafana.net/api/prom |
Basic instance_id:access_token or Bearer access_token |
encoded in token |
| Grafana datasource proxy |
${GRAFANA}/api/datasources/proxy/uid/<ds-uid> |
Grafana SA token |
datasource-configured |
| In-cluster via k8s apiserver |
https://kubernetes.default.svc/…/services/prometheus-k8s:web/proxy |
mounted ServiceAccount token + CA |
n/a |
Probe which one:
curl -sG "$URL/api/v1/status/buildinfo" # Prom: full payload; Mimir: also full, product field distinguishes
curl -s "$URL/ready" # Mimir health
curl -sG "$URL/api/v1/status/config" # Mimir returns empty; Prom returns loaded YAML
Missing X-Scope-OrgID on Mimir → 401/403 with a message mentioning "org id" — the tell.
Full connection and auth recipes in references/mimir-api.md. MCP-server options, Grafana-side SA tokens, and the when-to-skip-MCP rule are in references/agent-workflow.md.
The discovery → query loop
Do not start from rate(http_requests_total[5m]) on faith. A metric thought to exist may actually be called nginx_http_requests_total, istio_requests_total, or app_requests_total{job="api-server"}. Always walk this ladder:
- Catalog.
GET /api/v1/label/__name__/values — filter by prefix (http_, kube_, node_, container_, grpc_, process_, service name). Cache the result in the session.
- Metadata.
GET /api/v1/metadata?metric=<name> — type, help, unit. Confirms what the metric means on this cluster.
- Labels.
GET /api/v1/series?match[]=<metric> — enumerate label sets. For one dimension, GET /api/v1/label/<label>/values?match[]=<metric> is cheaper.
- Aliveness.
GET /api/v1/query?query=<metric> — samples right now? A series present in /series can still be silent. Always pair with up{job="…"}.
- Shape.
GET /api/v1/query_range at step=60s for an hour — eyeball the trajectory.
- Aggregate.
sum by(dim)(rate(<metric>[5m])) — drop noise, keep the dimension that answers the question.
- Correlate. Join metadata via
* on(instance) group_left(version) app_build_info — the canonical PromQL join.
- Threshold. Compare to a baseline:
offset 1w, recording rule, alert rule (/api/v1/rules).
Budget: step ≥ 30s for dashboards, ≥ 60s for exploration, ≥ 300s for multi-day windows. limit=… (Prom 3+) caps /query result cardinality. Don't scan with {__name__=~".+"}. Mimir 422 means the limit kicked in — never retry unchanged, change the query. Full cost and error-handling table in references/agent-workflow.md §5, §9.
Triage scripts by symptom
Full PromQL for each in references/agent-workflow.md §2. The one-liner reminders:
| Symptom |
Start with |
| Service 5xx |
rate of 5xx ÷ total, topk by handler, correlate by version via group_left(version) app_build_info |
| Slow p99 |
histogram_quantile on _bucket rates, top handler, then CPU saturation + GC + dependency latency |
| OOM |
container_memory_working_set_bytes / limits ratio, deriv(...[1h]) for leak |
| Disk fill |
predict_linear(node_filesystem_avail_bytes[1h], 4*3600) < 0 |
| HPA flap |
changes(kube_hpa_status_current_replicas[1h]) > 6 |
| Node not ready |
kube_node_status_condition{condition="Ready",status="true"} == 0, `up{job=~"kubelet |
PromQL — the things agents get wrong
Full reference in references/promql.md. The trap list:
rate before aggregation. sum(rate(x[5m])) is correct; rate(sum(x)[5m]) loses counter-reset detection. Agents invert this regularly.
- Histogram quantile of averages.
histogram_quantile must consume rates of _bucket series, aggregated by le plus any other dimensions needed. avg(_bucket) is wrong.
rate() window too small. rate(x[1m]) with 15s scrape = 4 samples; a single missed scrape poisons it. Use [5m] as the floor, or $__rate_interval in Grafana.
irate in alerts. Non-deterministic over a window; use rate.
topk in range queries. Non-deterministic per step — topk ranks per evaluation point, so the identity of the top-K series flips. Fine for instant queries and tables; wrong for graphs.
up == 1 ≠ healthy. Says scrape worked. The service can 500 every request.
- Mean latency.
rate(_sum)/rate(_count) hides the tail. Always histogram_quantile.
- Regex not anchored. It is.
=~"foo" means ^foo$ — write =~"foo.*" for prefix match.
- Counter on gauge.
rate() on a gauge produces garbage. delta/deriv are the gauge equivalents.
absent() with matchers that don't really exist. absent(up{job="api", pod="abc"}) fires even if pod="abc" never existed. Keep deadman-switch queries minimal.
- Staleness marker. Default
--query.lookback-delta=5m. Series absent >5m disappear from /query results even if in /series list.
- Mimir limits. 422 after a tight query means
max_samples_per_query or max_query_length tripped. Raise step, shrink window, tighten matchers.
Canonical join (agents underuse this — write it down):
sum by (version) (
rate(http_requests_total{status=~"5.."}[5m])
* on(instance) group_left(version) app_build_info
)
Grafana — what the agent is actually doing
Three workflows matter:
A. Query metrics through Grafana (agent has Grafana token but not Prom URL)
curl -sG -H "Authorization: Bearer $GF_TOKEN" \
"$GRAFANA/api/datasources/proxy/uid/$DS_UID/api/v1/query" \
--data-urlencode 'query=up'
Grafana enforces datasource RBAC. /api/datasources lists available datasources — find the Prometheus/Mimir one by type.
B. Build or fix a dashboard
- Fetch:
GET /api/dashboards/uid/<uid> → {dashboard, meta}.
- Mutate the
panels[].targets[].expr, fieldConfig.defaults.unit, legendFormat, templating.list[], etc.
- Push back:
POST /api/dashboards/db with the whole dashboard object (keep version from meta.version), folderUid, overwrite: false, message describing the agent's change.
The legacy /api/dashboards/db endpoint is still the default in 2026 tooling (Helm charts, grafana-operator, most MCP servers). Grafana 12 added — and Grafana 13 (current stable; 13.0 GA 2026-04-17, 13.1 2026-07-01) formally deprecated the legacy /api path in favor of — a Kubernetes-style /apis/dashboard.grafana.app/v1beta1/namespaces/<ns>/dashboards/<uid> API with resourceVersion concurrency. Legacy /api/dashboards/db remains fully functional; removal is deferred to a future major — keep using it unless the environment is already on the new API.
Full JSON schema, panel anatomy, transformation catalog, variable interpolation specifiers, and the full bug-fix list in references/grafana-dashboards.md.
C. Annotate what the agent did
Every automated intervention should leave a trail:
curl -X POST -H "Authorization: Bearer $GF_TOKEN" \
-H 'Content-Type: application/json' \
"$GRAFANA/api/annotations" \
-d "{\"time\": $(date +%s%3N), \"tags\":[\"agent\",\"api\"], \"text\":\"scaled api 3→6 after 5xx spike\"}"
This is how post-mortems stay tractable when agents start acting on metrics.
KPIs — what to actually measure
The judgment call the agent must make before picking metrics:
| Target |
Framework |
What to graph |
| A service (HTTP / gRPC / queue handler) |
RED |
Rate, Errors, Duration (p50/p95/p99) |
| A resource (CPU, mem, disk, GPU, pool) |
USE |
Utilization, Saturation, Errors |
| User experience |
Golden Signals + SLO |
Latency, Traffic, Errors, Saturation — wrapped in an availability/latency SLI |
| Alert trigger |
Multi-window multi-burn-rate |
14.4× over 1h + 5m, 6× over 6h + 30m, 3× over 1d + 2h, 1× over 3d + 6h |
Never alert on single thresholds, never average latency, never aggregate errors across every route — always decompose. up == 1 is not a health signal; a synthetic probe from outside the cluster is. Framework reference, exporter catalogs, anti-patterns, and the "what should I measure for X?" decision tree in references/kpis-frameworks.md.
The top 10 diagnostic PromQL queries
When dropped into an unfamiliar cluster, start with these:
# 1. Scrape health across all jobs
sum by (job) (up)
# 2. Which scrape jobs have failing targets
sum by (job) (up == 0)
# 3. Top-10 metrics by cardinality (Mimir status)
# -> GET /api/v1/status/tsdb
# 4. RPS per service (auto-discovers whatever exists)
sum by (job) (rate({__name__=~".+_requests_total"}[5m]))
# 5. Error rate per service
sum by (job) (rate({__name__=~".+_requests_total", status=~"5.."}[5m]))
/ sum by (job) (rate({__name__=~".+_requests_total"}[5m]))
# 6. p99 latency per service (classic histograms)
histogram_quantile(0.99,
sum by (job, le) (rate({__name__=~".+_duration_seconds_bucket"}[5m])))
# 7. Pods crashlooping
kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"} == 1
# 8. OOM-killed recently
sum by (namespace, pod) (
kube_pod_container_status_last_terminated_reason{reason="OOMKilled"})
# 9. Nodes with pressure
kube_node_status_condition{status="true",
condition=~"MemoryPressure|DiskPressure|PIDPressure"}
# 10. Anything going to fill its disk in 4 hours
predict_linear(node_filesystem_avail_bytes{
fstype!~"tmpfs|overlay|squashfs"}[1h], 4*3600) < 0
Queries 4-6 use __name__ wildcard — that's the "agent dropped into an unfamiliar cluster" shape. On production dashboards, prefer the actual metric name.
Reference map
- references/promql.md — full PromQL surface and the Prometheus HTTP API.
- references/mimir-api.md — Mimir architecture, endpoints, auth, per-tenant limits, curl recipes.
- references/grafana-dashboards.md — dashboard JSON, variables, transformations, both dashboard APIs, provisioning, bug-fix catalog.
- references/kpis-frameworks.md — RED / USE / Golden Signals, SLO burn-rate, exporter metric catalogs, anti-patterns, dashboard recipes, decision tree.
- references/agent-workflow.md — runtime playbook, triage scripts, MCP servers, auth patterns, error-handling table, curl snippets.
- references/sources.md — dated upstream sources for freshen mode.
1---2name: prometheus-mimir-grafana3description: Query Prometheus and Grafana Mimir, write and debug PromQL, and build or fix Grafana dashboards — for agents solving problems from metrics. Covers the Prometheus HTTP API (`/api/v1/query`, `query_range`, `series`, `labels`, `metadata`), Mimir multi-tenancy (`X-Scope-OrgID`, federation `a|b|c`, per-tenant 422/429 limits), the PromQL surface (selectors, rate family, classic + native histograms, `histogram_quantile`, vector matching `on()`/`group_left`, recording rules), Grafana dashboard JSON (panels, targets, variables + interpolation specifiers, legacy `/api/dashboards/db` vs Grafana-12 `/apis/dashboard.grafana.app/v1beta1/…`), KPI frameworks (RED, USE, Golden Signals, SLO burn-rate), connection recipes, MCP servers vs curl, and the PromQL trap list.4---56# Prometheus, Mimir, and Grafana — for agents78Target audience: an AI agent (or a human working through one) that has to *do things with metrics* — query, triage, alert, build and fix dashboards, and pick the right KPIs — against a stack that runs Prometheus, Grafana Mimir, and/or Grafana. Works whether the agent is given curl access to a Mimir gateway, an MCP server wrapper, or just a Grafana URL and a service-account token.910**This skill uses the stack; it does not change it.** Siblings in the11`observability` plugin own the lifecycle: version-laddering a Mimir install is12**`mimir-upgrade`**, getting SNMP devices to produce metrics in the first place13is **`snmp-exporter`**, and logs — a different pipeline entirely — are14**`logging-operator`** (with **`rancher-logging-exit`** for migrating off the15Rancher-bundled chart). If a query returns nothing, check here first; if the16component itself is broken or stale, you are in the wrong skill.1718## Why this matters1920Metrics lie in three directions: (1) the agent queries the wrong metric or wrong label, (2) the query is syntactically fine but semantically broken (`rate` after aggregation, `histogram_quantile` of the mean, default histogram buckets sized for the wrong service), (3) the dashboard *looks* correct but the datasource variable is empty or the unit is off by 1000×. Each failure mode has an easy check. This skill is those checks, organized so the agent reaches for them before issuing the first query.2122## The one-paragraph rubric2324Prometheus stores samples identified by `metric_name{label=value, ...}`. Mimir is a horizontally-scalable multi-tenant store that speaks Prometheus's wire protocol and API under a `/prometheus/api/v1/…` prefix, gated by `X-Scope-OrgID`. Grafana is the UI and the dashboards-as-JSON store. PromQL returns instant vectors, range vectors, scalars, or strings; `rate()` always wraps a counter before any `sum`; `histogram_quantile()` always consumes aggregated `_bucket` rates; and `$__rate_interval` is the only interval variable safe for counter rates. RED answers *is my service OK*, USE answers *is my resource OK*, and Golden Signals + SLO burn-rate answer *are my users OK*. Everything else is detail in the references.2526## Connect first — figure out what endpoint is in front of the agent2728Before the first query, identify the endpoint and auth shape. One of these five patterns will match:2930| Shape | Base URL | Auth | Tenant |31|---|---|---|---|32| Prometheus direct | `http://prometheus:9090` | usually none / optional Basic | n/a |33| Self-hosted Mimir via gateway | `https://mimir.example.com/prometheus` | `Authorization: Bearer …` | `X-Scope-OrgID: <tenant>` |34| Grafana Cloud Mimir | `https://prometheus-prod-XX.grafana.net/api/prom` | Basic `instance_id:access_token` **or** `Bearer access_token` | encoded in token |35| Grafana datasource proxy | `${GRAFANA}/api/datasources/proxy/uid/<ds-uid>` | Grafana SA token | datasource-configured |36| In-cluster via k8s apiserver | `https://kubernetes.default.svc/…/services/prometheus-k8s:web/proxy` | mounted ServiceAccount token + CA | n/a |3738Probe which one:39```bash40curl -sG "$URL/api/v1/status/buildinfo" # Prom: full payload; Mimir: also full, product field distinguishes41curl -s "$URL/ready" # Mimir health42curl -sG "$URL/api/v1/status/config" # Mimir returns empty; Prom returns loaded YAML43```44Missing `X-Scope-OrgID` on Mimir → 401/403 with a message mentioning "org id" — the tell.4546Full connection and auth recipes in [references/mimir-api.md](references/mimir-api.md). MCP-server options, Grafana-side SA tokens, and the when-to-skip-MCP rule are in [references/agent-workflow.md](references/agent-workflow.md).4748## The discovery → query loop4950Do not start from `rate(http_requests_total[5m])` on faith. A metric *thought* to exist may actually be called `nginx_http_requests_total`, `istio_requests_total`, or `app_requests_total{job="api-server"}`. Always walk this ladder:51521. **Catalog.** `GET /api/v1/label/__name__/values` — filter by prefix (`http_`, `kube_`, `node_`, `container_`, `grpc_`, `process_`, service name). Cache the result in the session.532. **Metadata.** `GET /api/v1/metadata?metric=<name>` — type, help, unit. Confirms what the metric *means* on this cluster.543. **Labels.** `GET /api/v1/series?match[]=<metric>` — enumerate label sets. For one dimension, `GET /api/v1/label/<label>/values?match[]=<metric>` is cheaper.554. **Aliveness.** `GET /api/v1/query?query=<metric>` — samples *right now*? A series present in `/series` can still be silent. Always pair with `up{job="…"}`.565. **Shape.** `GET /api/v1/query_range` at `step=60s` for an hour — eyeball the trajectory.576. **Aggregate.** `sum by(dim)(rate(<metric>[5m]))` — drop noise, keep the dimension that answers the question.587. **Correlate.** Join metadata via `* on(instance) group_left(version) app_build_info` — the canonical PromQL join.598. **Threshold.** Compare to a baseline: `offset 1w`, recording rule, alert rule (`/api/v1/rules`).6061Budget: `step` ≥ 30s for dashboards, ≥ 60s for exploration, ≥ 300s for multi-day windows. `limit=…` (Prom 3+) caps `/query` result cardinality. Don't scan with `{__name__=~".+"}`. Mimir 422 means the limit kicked in — **never retry unchanged**, change the query. Full cost and error-handling table in [references/agent-workflow.md](references/agent-workflow.md) §5, §9.6263## Triage scripts by symptom6465Full PromQL for each in [references/agent-workflow.md](references/agent-workflow.md) §2. The one-liner reminders:6667| Symptom | Start with |68|---|---|69| Service 5xx | rate of 5xx ÷ total, topk by handler, correlate by version via `group_left(version) app_build_info` |70| Slow p99 | histogram_quantile on `_bucket` rates, top handler, then CPU saturation + GC + dependency latency |71| OOM | `container_memory_working_set_bytes` / limits ratio, `deriv(...[1h])` for leak |72| Disk fill | `predict_linear(node_filesystem_avail_bytes[1h], 4*3600) < 0` |73| HPA flap | `changes(kube_hpa_status_current_replicas[1h]) > 6` |74| Node not ready | `kube_node_status_condition{condition="Ready",status="true"} == 0`, `up{job=~"kubelet|node-exporter"}` |7576## PromQL — the things agents get wrong7778Full reference in [references/promql.md](references/promql.md). The trap list:7980- **`rate` before aggregation.** `sum(rate(x[5m]))` is correct; `rate(sum(x)[5m])` loses counter-reset detection. Agents invert this regularly.81- **Histogram quantile of averages.** `histogram_quantile` must consume **rates of `_bucket` series**, aggregated **by `le`** plus any other dimensions needed. `avg(_bucket)` is wrong.82- **`rate()` window too small.** `rate(x[1m])` with 15s scrape = 4 samples; a single missed scrape poisons it. Use `[5m]` as the floor, or `$__rate_interval` in Grafana.83- **`irate` in alerts.** Non-deterministic over a window; use `rate`.84- **`topk` in range queries.** Non-deterministic per step — `topk` ranks per evaluation point, so the *identity* of the top-K series flips. Fine for instant queries and tables; wrong for graphs.85- **`up == 1` ≠ healthy.** Says scrape worked. The service can 500 every request.86- **Mean latency.** `rate(_sum)/rate(_count)` hides the tail. Always `histogram_quantile`.87- **Regex not anchored.** It is. `=~"foo"` means `^foo$` — write `=~"foo.*"` for prefix match.88- **Counter on gauge.** `rate()` on a gauge produces garbage. `delta`/`deriv` are the gauge equivalents.89- **`absent()` with matchers that don't really exist.** `absent(up{job="api", pod="abc"})` fires even if `pod="abc"` never existed. Keep deadman-switch queries minimal.90- **Staleness marker.** Default `--query.lookback-delta=5m`. Series absent >5m disappear from `/query` results even if in `/series` list.91- **Mimir limits.** 422 after a tight query means `max_samples_per_query` or `max_query_length` tripped. Raise `step`, shrink window, tighten matchers.9293Canonical join (agents underuse this — write it down):94```promql95sum by (version) (96 rate(http_requests_total{status=~"5.."}[5m])97 * on(instance) group_left(version) app_build_info98)99```100101## Grafana — what the agent is actually doing102103Three workflows matter:104105### A. Query metrics *through* Grafana (agent has Grafana token but not Prom URL)106```bash107curl -sG -H "Authorization: Bearer $GF_TOKEN" \108 "$GRAFANA/api/datasources/proxy/uid/$DS_UID/api/v1/query" \109 --data-urlencode 'query=up'110```111Grafana enforces datasource RBAC. `/api/datasources` lists available datasources — find the Prometheus/Mimir one by `type`.112113### B. Build or fix a dashboard1141. Fetch: `GET /api/dashboards/uid/<uid>` → `{dashboard, meta}`.1152. Mutate the `panels[].targets[].expr`, `fieldConfig.defaults.unit`, `legendFormat`, `templating.list[]`, etc.1163. Push back: `POST /api/dashboards/db` with the whole `dashboard` object (keep `version` from `meta.version`), `folderUid`, `overwrite: false`, `message` describing the agent's change.117118The legacy `/api/dashboards/db` endpoint is still the default in 2026 tooling (Helm charts, grafana-operator, most MCP servers). Grafana 12 added — and Grafana 13 (current stable; 13.0 GA 2026-04-17, 13.1 2026-07-01) formally deprecated the legacy `/api` path in favor of — a Kubernetes-style `/apis/dashboard.grafana.app/v1beta1/namespaces/<ns>/dashboards/<uid>` API with `resourceVersion` concurrency. Legacy `/api/dashboards/db` remains fully functional; removal is deferred to a future major — keep using it unless the environment is already on the new API.119120Full JSON schema, panel anatomy, transformation catalog, variable interpolation specifiers, and the full bug-fix list in [references/grafana-dashboards.md](references/grafana-dashboards.md).121122### C. Annotate what the agent did123Every automated intervention should leave a trail:124```bash125curl -X POST -H "Authorization: Bearer $GF_TOKEN" \126 -H 'Content-Type: application/json' \127 "$GRAFANA/api/annotations" \128 -d "{\"time\": $(date +%s%3N), \"tags\":[\"agent\",\"api\"], \"text\":\"scaled api 3→6 after 5xx spike\"}"129```130This is how post-mortems stay tractable when agents start acting on metrics.131132## KPIs — what to actually measure133134The judgment call the agent must make *before* picking metrics:135136| Target | Framework | What to graph |137|---|---|---|138| A service (HTTP / gRPC / queue handler) | **RED** | Rate, Errors, Duration (p50/p95/p99) |139| A resource (CPU, mem, disk, GPU, pool) | **USE** | Utilization, Saturation, Errors |140| User experience | **Golden Signals + SLO** | Latency, Traffic, Errors, Saturation — wrapped in an availability/latency SLI |141| Alert trigger | **Multi-window multi-burn-rate** | 14.4× over 1h + 5m, 6× over 6h + 30m, 3× over 1d + 2h, 1× over 3d + 6h |142143Never alert on single thresholds, never average latency, never aggregate errors across every route — always decompose. `up == 1` is not a health signal; a synthetic probe from outside the cluster is. Framework reference, exporter catalogs, anti-patterns, and the "what should I measure for X?" decision tree in [references/kpis-frameworks.md](references/kpis-frameworks.md).144145## The top 10 diagnostic PromQL queries146147When dropped into an unfamiliar cluster, start with these:148149```promql150# 1. Scrape health across all jobs151sum by (job) (up)152153# 2. Which scrape jobs have failing targets154sum by (job) (up == 0)155156# 3. Top-10 metrics by cardinality (Mimir status)157# -> GET /api/v1/status/tsdb158159# 4. RPS per service (auto-discovers whatever exists)160sum by (job) (rate({__name__=~".+_requests_total"}[5m]))161162# 5. Error rate per service163sum by (job) (rate({__name__=~".+_requests_total", status=~"5.."}[5m]))164 / sum by (job) (rate({__name__=~".+_requests_total"}[5m]))165166# 6. p99 latency per service (classic histograms)167histogram_quantile(0.99,168 sum by (job, le) (rate({__name__=~".+_duration_seconds_bucket"}[5m])))169170# 7. Pods crashlooping171kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"} == 1172173# 8. OOM-killed recently174sum by (namespace, pod) (175 kube_pod_container_status_last_terminated_reason{reason="OOMKilled"})176177# 9. Nodes with pressure178kube_node_status_condition{status="true",179 condition=~"MemoryPressure|DiskPressure|PIDPressure"}180181# 10. Anything going to fill its disk in 4 hours182predict_linear(node_filesystem_avail_bytes{183 fstype!~"tmpfs|overlay|squashfs"}[1h], 4*3600) < 0184```185186Queries 4-6 use `__name__` wildcard — that's the "agent dropped into an unfamiliar cluster" shape. On production dashboards, prefer the actual metric name.187188## Reference map189190- [references/promql.md](references/promql.md) — full PromQL surface and the Prometheus HTTP API.191- [references/mimir-api.md](references/mimir-api.md) — Mimir architecture, endpoints, auth, per-tenant limits, curl recipes.192- [references/grafana-dashboards.md](references/grafana-dashboards.md) — dashboard JSON, variables, transformations, both dashboard APIs, provisioning, bug-fix catalog.193- [references/kpis-frameworks.md](references/kpis-frameworks.md) — RED / USE / Golden Signals, SLO burn-rate, exporter metric catalogs, anti-patterns, dashboard recipes, decision tree.194- [references/agent-workflow.md](references/agent-workflow.md) — runtime playbook, triage scripts, MCP servers, auth patterns, error-handling table, curl snippets.195- [references/sources.md](references/sources.md) — dated upstream sources for freshen mode.