Skill: Nightingale (N9E) Modify an Existing Dashboard
Help users modify an existing dashboard using natural language — not create a new one. Three typical kinds of requests:
| Request |
What you change |
| Change variables |
A template variable's value expression (definition), default value, whether it is multi-select (multi), display label (label) |
| Check and fix variables |
Scan variable definitions and the references to variables inside charts, detect smells (a chart references an undefined variable, a variable returns no value, datasource references are inconsistent, etc.) and fix them |
| Change chart series |
The series of a chart (panel): query expression (PromQL), legend, unit, adding/removing series, changing the title, changing the chart type (e.g., stat→timeseries) |
Editing series queries (queries) is only supported for Prometheus/VictoriaMetrics panels; SQL/log panels (mysql, ck, es, etc.) can only have their unit, title, description changed or be deleted — passing queries will be rejected by the tool.
Iron rule: a single proposal call wraps it up; confirmation is done by the system
update_dashboard is a proposal-style write: after you call it (passing only the part you want to change), the tool computes the diff, directly shows the user the list of changes and pauses the conversation; once the user confirms, the system automatically persists it to the database — the confirmation step neither needs nor goes through you. Therefore:
- First call
get_dashboard_detail(id, include_config=true) to read the current variables, chart summaries, and variable health check.
- After working out the part you want to change, call
update_dashboard once, passing only the variables/panels/fix_datasource you want to change. This call is the last step of this turn; the system takes over the display and confirmation.
- Do not render the change table yourself (the system will show the list generated by the tool), and do not pass
proposal_id/confirmed (those are parameters for the system's confirmation channel).
- When the user rejects or raises a new requirement, you will receive feedback in a new turn: just recompute the changes per the feedback and call
update_dashboard again (the old proposal is automatically voided).
Step 1: Locate the dashboard
- If the context already carries
dashboard_id (injected by the frontend from /dashboards/<id>, or already determined in a previous turn) → use it directly, don't call list_dashboards again.
- The user pasted a
/dashboards/<id> link → take the id from it.
- Only a name was given → call
list_dashboards(query="...") to match by name; if there are multiple candidates or no match, list them and ask the user — do not guess.
Read the business group and the datasources from the dashboard itself; do not ask the user for them.
Step 2: Read the current state (always pass include_config=true)
get_dashboard_detail(id=<id>, include_config=true)
In the response:
variables: for each variable, name / type / label / definition / multi / default_value / datasource_value
panels: for each chart, id / name / type / unit / queries (each series contains ref / promql / legend / instant / step / hide; fields that are not set are omitted); charts inside collapsed rows (row) have been flattened in
variable_lint: the list of smells caught by the variable health check (e.g., "the query expression of chart X references an undefined variable $foo")
Prefer id when locating a chart (e.g., panel-3); names may be duplicated.
Step 3: Validate that the query can return a value (optional but recommended)
When changing PromQL or fixing a variable's definition, you can first use query_prometheus / list_metrics / get_metric_labels to verify that the new expression really has data before proposing, to avoid the chart still being empty after the change.
For variable-fix tasks: for each item caught in variable_lint, decide on a fix action (change the definition / rename the reference / fix the datasource reference) and put them all into the same proposal.
Step 4: Submit the proposal (call update_dashboard, just once)
Call update_dashboard, passing only the part you want to change (everything else is preserved as-is; the tool won't touch it). The tool will show the user the list of changes and wait for confirmation; this turn ends here:
- Change/add/delete variables →
variables (a JSON array, matched by name):[{"name":"ident","default_value":"web01","multi":false}]
- Write only the fields you want to change; if
name does not exist it is treated as adding a new query variable, and an addition must carry definition (omitting it raises an error — a misspelled name falls into the addition branch, and this guard catches it); delete:true deletes it.
- Change chart series →
panels (a JSON array, located by id first, otherwise by name):[{"id":"panel-2","unit":"percent","queries":[{"promql":"avg(cpu_usage_active{cpu=\"cpu-total\",ident=~\"$ident\"})","legend":"{{ident}}"}]}]
- When
queries is passed in it is incrementally merged with the existing series: matched by ref (the original series' refId), only the fields you write are overwritten; the rest (step/hide/mode, the refId-associated overrides, etc.) are preserved as-is; anything without a ref is always treated as a new series (no position match). Existing series that do not appear in queries will not be deleted and are preserved as-is — so when changing only one series, pass just that one (with its ref); you don't need to list all the series of the whole chart.
- Changing an existing series must carry its
ref (without ref it adds a series instead of changing the original one); to delete a series, put its ref on that series item and write delete:true.
new_name changes the title, unit changes the unit, description changes the description, type changes the chart type (only timeseries/stat/gauge/barGauge/pie/table; changing the type resets the chart's type-style options to the defaults of the new type, and changing to timeseries also clears the instant flag on series to restore range queries; a row layout row cannot be changed), delete:true (on the panel item) deletes the whole chart. Not passing queries leaves the series untouched.
- Only the fields above are valid inside a panel item: colors, thresholds, layout coordinates, and other fields are not supported. A patch that only contains unsupported fields is directly rejected by the tool; if mixed in among supported fields, the unsupported parts are silently dropped — so you must restate to the user strictly based on the change list returned by the tool; changes outside that list did not happen. When the user wants to change something unsupported, just say it can't be done and suggest editing it manually on the page.
- Fix datasource references →
fix_datasource: true: re-points dangling or hard-coded datasource references in charts/variables uniformly to the dashboard's datasource variable. Suitable for fixing smells like "chart returns no data / datasource references are inconsistent."
Step 5: Responding to special cases
- After reading the current state, if you find no change is needed, the query validation returns no data, or the target chart/variable cannot be located: do not call
update_dashboard; just state the reason in a single sentence and give a suggestion.
- When the user gives feedback in a new turn that "the proposal is stale/invalid" (the dashboard was changed by someone else in the meantime): just re-read the current state and re-propose.
The changes list returned by the tool is the set of changes actually persisted to the database; restate to the user based on it.
Notes
- For multi-select variables, use
=~ instead of = in PromQL, e.g., ident=~"$ident".
- When changing/adding a series, use the
{{label}} form for the legend template, e.g., {{ident}}.
- When unsure of a metric name/label, probe with
list_metrics / get_metric_labels first; don't make it up.
- When the user raises multiple changes in a row within one conversation, you can merge them into a single
update_dashboard call (pass variables and panels together); the change list and confirmation are gated by the system and won't be skipped.
1---2name: modify-dashboard3description: Modify an existing monitoring dashboard on Nightingale (n9e). Use when the user asks to change a dashboard's variables, check and fix variables, modify charts/series (change PromQL, legend, unit, add/remove series), rename a chart, or change the chart type (e.g., turn a stat chart into a timeseries chart). Distinct from "creating a dashboard from scratch" (that is create-dashboard).4---56# Skill: Nightingale (N9E) Modify an Existing Dashboard78Help users **modify an existing dashboard** using natural language — not create a new one. Three typical kinds of requests:910| Request | What you change |11|------|-------------|12| **Change variables** | A template variable's value expression (definition), default value, whether it is multi-select (multi), display label (label) |13| **Check and fix variables** | Scan variable definitions and the references to variables inside charts, detect smells (a chart references an undefined variable, a variable returns no value, datasource references are inconsistent, etc.) and fix them |14| **Change chart series** | The series of a chart (panel): query expression (PromQL), legend, unit, adding/removing series, changing the title, changing the chart type (e.g., stat→timeseries) |1516> Editing series queries (`queries`) is **only supported for Prometheus/VictoriaMetrics panels**; SQL/log panels (mysql, ck, es, etc.) can only have their unit, title, description changed or be deleted — passing `queries` will be rejected by the tool.1718## Iron rule: a single proposal call wraps it up; confirmation is done by the system1920`update_dashboard` is a **proposal-style write**: after you call it (passing only the part you want to change), the tool computes the diff, **directly shows the user the list of changes and pauses the conversation**; once the user confirms, the system automatically persists it to the database — the confirmation step neither needs nor goes through you. Therefore:21221. First call `get_dashboard_detail(id, include_config=true)` to read the current variables, chart summaries, and variable health check.232. After working out the part you want to change, call `update_dashboard` once, **passing only** the `variables`/`panels`/`fix_datasource` you want to change. This call is the last step of this turn; the system takes over the display and confirmation.243. **Do not** render the change table yourself (the system will show the list generated by the tool), and **do not** pass `proposal_id`/`confirmed` (those are parameters for the system's confirmation channel).254. When the user rejects or raises a new requirement, you will receive feedback in a new turn: just recompute the changes per the feedback and call `update_dashboard` again (the old proposal is automatically voided).2627## Step 1: Locate the dashboard2829- If the context already carries `dashboard_id` (injected by the frontend from `/dashboards/<id>`, or already determined in a previous turn) → use it directly, don't call `list_dashboards` again.30- The user pasted a `/dashboards/<id>` link → take the id from it.31- Only a name was given → call `list_dashboards(query="...")` to match by name; if there are multiple candidates or no match, list them and ask the user — **do not** guess.3233Read the business group and the datasources from the dashboard itself; **do not** ask the user for them.3435## Step 2: Read the current state (always pass include_config=true)3637```38get_dashboard_detail(id=<id>, include_config=true)39```4041In the response:42- `variables`: for each variable, `name / type / label / definition / multi / default_value / datasource_value`43- `panels`: for each chart, `id / name / type / unit / queries` (each series contains `ref / promql / legend / instant / step / hide`; fields that are not set are omitted); charts inside collapsed rows (row) have been flattened in44- `variable_lint`: the list of smells caught by the variable health check (e.g., "the query expression of chart X references an undefined variable $foo")4546**Prefer `id` when locating a chart** (e.g., `panel-3`); names may be duplicated.4748## Step 3: Validate that the query can return a value (optional but recommended)4950When changing PromQL or fixing a variable's definition, you can first use `query_prometheus` / `list_metrics` / `get_metric_labels` to verify that the new expression really has data before proposing, to avoid the chart still being empty after the change.5152For variable-fix tasks: for each item caught in `variable_lint`, decide on a fix action (change the definition / rename the reference / fix the datasource reference) and put them all into the same proposal.5354## Step 4: Submit the proposal (call update_dashboard, just once)5556Call `update_dashboard`, **passing only** the part you want to change (everything else is preserved as-is; the tool won't touch it). The tool will show the user the list of changes and wait for confirmation; this turn ends here:5758- **Change/add/delete variables** → `variables` (a JSON array, matched by `name`):59 ```json60 [{"name":"ident","default_value":"web01","multi":false}]61 ```62 - Write only the fields you want to change; if `name` does not exist it is treated as adding a new query variable, and **an addition must carry `definition`** (omitting it raises an error — a misspelled name falls into the addition branch, and this guard catches it); `delete:true` deletes it.63- **Change chart series** → `panels` (a JSON array, located by `id` first, otherwise by `name`):64 ```json65 [{"id":"panel-2","unit":"percent","queries":[{"promql":"avg(cpu_usage_active{cpu=\"cpu-total\",ident=~\"$ident\"})","legend":"{{ident}}"}]}]66 ```67 - When `queries` is passed in it is **incrementally merged** with the existing series: matched by `ref` (the original series' refId), only the fields you write are overwritten; the rest (step/hide/__mode__, the refId-associated overrides, etc.) are preserved as-is; **anything without a `ref` is always treated as a new series (no position match)**. **Existing series that do not appear in `queries` will not be deleted and are preserved as-is** — so when changing only one series, pass just that one (with its `ref`); you don't need to list all the series of the whole chart.68 - **Changing an existing series must carry its `ref`** (without `ref` it adds a series instead of changing the original one); to delete a series, put its `ref` on that series item and write `delete:true`.69 - `new_name` changes the title, `unit` changes the unit, `description` changes the description, `type` changes the chart type (only `timeseries`/`stat`/`gauge`/`barGauge`/`pie`/`table`; changing the type resets the chart's type-style options to the defaults of the new type, and changing to `timeseries` also clears the `instant` flag on series to restore range queries; a row layout row cannot be changed), `delete:true` (on the panel item) deletes the whole chart. Not passing `queries` leaves the series untouched.70 - **Only the fields above are valid inside a panel item**: colors, thresholds, layout coordinates, and other fields are not supported. A patch that **only contains** unsupported fields is directly rejected by the tool; if mixed in among supported fields, the unsupported parts are silently dropped — so you **must restate to the user strictly based on the change list returned by the tool**; changes outside that list did not happen. When the user wants to change something unsupported, just say it can't be done and suggest editing it manually on the page.71- **Fix datasource references** → `fix_datasource: true`: re-points dangling or hard-coded datasource references in charts/variables uniformly to the dashboard's datasource variable. Suitable for fixing smells like "chart returns no data / datasource references are inconsistent."7273## Step 5: Responding to special cases7475- After reading the current state, if you find **no change is needed**, the query validation **returns no data**, or the target chart/variable **cannot be located**: do not call `update_dashboard`; just state the reason in a single sentence and give a suggestion.76- When the user gives feedback in a new turn that "the proposal is stale/invalid" (the dashboard was changed by someone else in the meantime): just re-read the current state and re-propose.7778The `changes` list returned by the tool is the set of changes actually persisted to the database; restate to the user based on it.7980## Notes8182- For multi-select variables, use `=~` instead of `=` in PromQL, e.g., `ident=~"$ident"`.83- When changing/adding a series, use the `{{label}}` form for the legend template, e.g., `{{ident}}`.84- When unsure of a metric name/label, probe with `list_metrics` / `get_metric_labels` first; don't make it up.85- When the user raises multiple changes in a row within one conversation, you can merge them into a single `update_dashboard` call (pass `variables` and `panels` together); the change list and confirmation are gated by the system and won't be skipped.