Skip to content

dashboard

**Scope**: Dashboard-as-Code (DaC) patterns, Go/CUE SDK usage, percli CLI, variables, panels, and datasource wiring. Does not cover operator CRDs or plugin development. **Version range**: Perses v0.47+ (CRD v1alpha2, Go SDK v0.47+) **Generated**: 2026-05-09 — verify against

From plugin
vexjoy-agent
413198 skills198 agents10 commands86 hooks
Install
$ npx -y skills add notque/vexjoy-agent --agent claude-code

How it fires

How this agent gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.

Context preview

The summary Claude sees to decide when to auto-load this agent.

**Scope**: Dashboard-as-Code (DaC) patterns, Go/CUE SDK usage, percli CLI, variables, panels, and datasource wiring. Does not cover operator CRDs or plugin development. **Version range**: Perses v0.47+ (CRD v1alpha2, Go SDK v0.47+) **Generated**: 2026-05-09 — verify against

Agent definition

dashboard.md

Perses Dashboard Reference

> **Scope**: Dashboard-as-Code (DaC) patterns, Go/CUE SDK usage, percli CLI, variables, panels, and datasource wiring. Does not cover operator CRDs or plugin development. > **Version range**: Perses v0.47+ (CRD v1alpha2, Go SDK v0.47+) > **Generated**: 2026-05-09 — verify against https://github.com/perses/perses/releases

---

Overview

Perses dashboards are defined as JSON/YAML documents validated against CUE schemas. Dashboard-as-Code (DaC) lets you generate these documents programmatically using the Go SDK or CUE, avoiding hand-edited JSON drift. The most common failure mode is referencing a datasource name or variable name that doesn't match the project's registered datasources — Perses fails silently on missing refs.

---

Pattern Table

| Pattern | Version | Use When | Avoid When | |---------|---------|----------|------------| | Go SDK (`go/sdk`) | `v0.47+` | Generating dashboards from code | The dashboard is one-off or manually curated | | CUE schemas | all | Validating dashboard JSON | You need Go type safety across multiple dashboards | | `percli apply -f` | all | Pushing dashboards to a live server | Dry-runs — use `--dry-run` flag | | `percli export` | all | Pulling existing dashboards to DaC | Initial migration from UI-created dashboards | | `percli migrate` | `v0.40+` | Converting Grafana dashboards | Only Grafana JSON v8+ is supported |

---

Dashboard-as-Code: Go SDK

Minimal working dashboard

import (
    "github.com/perses/perses/go/sdk/dashboard"
    "github.com/perses/perses/go/sdk/panel/timeseries"
    listvar "github.com/perses/perses/go/sdk/variable/listvar"
    prometheustarget "github.com/perses/perses/go/sdk/prometheus/query"
)

builder, err := dashboard.New("My Dashboard",
    dashboard.ProjectName("my-project"),
    dashboard.Duration("1h"),
    dashboard.RefreshInterval("30s"),
    dashboard.AddVariable(
        listvar.New("namespace",
            listvar.DisplayName("Namespace"),
            listvar.CapturingRegexp("(.+)"),
            listvar.AllowAllValue(true),
            listvar.AllowMultiple(false),
        ),
    ),
    dashboard.AddPanelGroup("Overview",
        dashboard.AddPanel("Requests/sec",
            timeseries.New("Requests/sec",
                timeseries.WithPrometheusTarget(
                    `rate(http_requests_total{namespace="$namespace"}[5m])`,
                    prometheustarget.Legend("{{handler}}"),
                ),
            ),
        ),
    ),
)

**Why**: The SDK enforces schema at compile time. Mistyped field names are caught before `percli apply` runs.

---

Variable types

// Text variable — free-form input
textvar.New("cluster",
    textvar.DisplayName("Cluster"),
    textvar.Value("prod"),  // default value
)

// List variable — query-driven options
listvar.New("namespace",
    listvar.PrometheusLabelValuesQuery("namespace", "kube_pod_info"),
    listvar.AllowAllValue(true),
    listvar.AllowMultiple(true),
    listvar.Sort(listvar.AlphabeticalAsc),
)

// Constant variable — fixed value, often hidden
constantvar.New("datasource",
    constantvar.Value("PrometheusDemo"),
    constantvar.Hide(true),
)

**Why**: List variables using `AllowMultiple(true)` must use `=~` (regex match) in PromQL, not `=`. Mixing `=` with a multi-value variable silently uses only the first selected value.

---

Panel types and imports

| Panel | Import path suffix | Key option | |-------|-------------|-----------| | TimeSeriesChart | `panel/timeseries` | `.WithPrometheusTarget()` | | GaugeChart | `panel/gauge` | `.Thresholds()` | | StatChart | `panel/stat` | `.Format()`, `.Sparkline()` | | BarChart | `panel/barchart` | `.XAxis()` | | Markdown | `panel/markdown` | `.Text()` | | ScatterChart | `panel/scatterchart` | `.WithPrometheusTarget()` | | Table | `panel/table` | `.ColumnSettings()` |

---

Pattern Catalog: Detection and Fixes

Hardcoded datasource name (breaks across projects)

**Detection**:

grep -rn '"default"' --include="*.go" | grep -i datasource
grep -rn 'datasource.*"prometheus"' --include="*.cue"
rg 'datasourceName.*"[A-Za-z]+"' --type go

**Signal**:

// Hardcoded datasource name — breaks when deployed to a project
// that registered the datasource under a different name
timeseries.WithPrometheusTarget(
    "up",
    prometheustarget.Datasource("default"),  // "default" may not exist
)

**Why it matters**: Datasource names are scoped to a project. A dashboard deployed to project `team-a` expecting `"default"` fails if that project registered the datasource as `"prometheus-prod"`. The panel renders empty with no error in the UI.

**Preferred action**:

// Use a variable reference for datasource selection
constantvar.New("datasource", constantvar.Value("PrometheusDemo"))
// Then reference it:
prometheustarget.Datasource("$datasource")

---

Multi-value variable with equality operator

**Detection**:

grep -rn 'AllowMultiple(true)' --include="*.go" -A5 | grep -v '=~'
rg '\$\w+[^~]' --type json | grep -i 'expr\|query'

**Signal**:

listvar.New("namespace", listvar.AllowMultiple(true))
// ...then in query:
`kube_pod_info{namespace="$namespace"}`  // = operator, not =~

**Why it matters**: When multiple namespaces are selected, `$namespace` expands to `ns1|ns2|ns3`. The `=` operator treats this as a literal string match, always returning no results. The `=~` operator applies regex matching.

**Preferred action**:

`kube_pod_info{namespace=~"$namespace"}`  // regex match for multi-value

---

Missing `dashboard.Duration` / `RefreshInterval`

**Detection**:

rg 'dashboard\.New\(' --type go -A 10 | grep -c 'Duration\|Refresh'
grep -rn 'dashboard.New(' --include="*.go" -A 8 | grep -v 'Duration'

**Signal**:

dashboard.New("My Dashboard",
    dashboard.ProjectName("my-project"),
    // No Duration or RefreshInterval — uses server defaults (may be 6h)
)

**Why it matters**: Wit

Read more
Ships withvexjoy-agent

Essays and writing behind this toolkit live at vexjoy.com. AI agents skip steps. "Looks correct" replaces running tests. "Trivial change" replaces verification.

Get the whole plugin, auto-invoked