Verify answers against a named BI dashboard in the user's local browser, capturing widget values and active filters with extraction tiers. Use for dashboard checks and context-interview Stage 5. Not for general browsing, credentials, or warehouse queries.
Scanned 9/4/2026
Install to Claude Code
npx -y skills add nodal-data/nodal-context --skill dashboard-verify --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Dashboard Verify?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/nodal-data-dashboard-verify)More formats (shields.io, HTML) on the badges page.
---
name: dashboard-verify
description: Verify answers against a named BI dashboard in the user's local browser, capturing widget values and active filters with extraction tiers. Use for dashboard checks and context-interview Stage 5. Not for general browsing, credentials, or warehouse queries.
---
# Dashboard Verify
Extract what a BI dashboard actually shows — values *and* the filter state that
makes those values mean something — into capture files another process (or a
human) can reconcile against. The analyst's browser does the showing; you do the
reading; the analyst stays the authority on what counts as truth.
Look for the nearest `.nodal.local.json` upward to the current git root and accept
only version 1. Treat its browser binding and capability timestamps as hints, and
respect its browser mode. Missing `browser.mode` is backward-compatible: infer
`automated` when a binding is set and `ask_when_needed` otherwise. Discover the
live tools again before use unless the saved mode is `manual`, which goes directly
to the human-read fallback. Missing, invalid, or stale config degrades to binding
discovery below. Never write configuration; only the explicitly invoked
`setup-nodal` skill may do so.
## Hard fences (non-negotiable)
- **Local browser only.** You attach to a browser on the user's machine via an MCP
binding (`references/browser-contract.md`). Never a hosted/remote browser.
- **Zero credentials.** If a login wall appears, hand control to the user: *"Please
log in — I'll continue when the dashboard is visible."* Wait on an explicit
completion condition (URL match / selector visible / cookie present — see the
contract doc). Never type, store, request, or even look at a credential; never
bypass a CAPTCHA or SSO prompt.
- **Not a browser agent.** The workflow is exactly: navigate to the named dashboard
→ enumerate widgets → capture filter state → extract values → emit captures.
No browsing beyond the dashboard's own pages, no clicking around "to explore",
no other sites.
- **Never fake precision.** Every value carries the `extraction_tier` it came from
(`references/capture-format.md`). A value read from a screenshot is labeled as
such; a display-rounded tile ("$3.7K") is recorded as display precision, not
invented decimals. When a tool bottoms out at vision tier, say so — downgrading
confidence visibly is correct behavior, not failure.
## Extraction hierarchy — read data, not pixels
Prefer, in order (tag every value with the tier that produced it):
1. **Network payloads** — the BI frontend fetches widget data as JSON; capture the
response bodies. Exact numbers.
2. *(1.5)* **Embedded data** — figure/config JSON already present in the page
(e.g. Plotly's `.data`), read via evaluate-js. Exact numbers, no network needed.
3. *(2)* **DOM / accessibility tree** — KPI tiles, tables, SVG charts, aria labels.
Exact for integers; display precision for formatted values.
4. *(3)* **Export clicks** — per-widget "download CSV". Slow but ground truth.
5. *(4)* **Vision on screenshots** — last resort, canvas-only charts. Use vision to
*locate* (find the widget, read the filter bar), not to *read* values, wherever
avoidable.
**Filter-state capture is mandatory, not optional.** A number without its active
filters, date grain, and window anchor is meaningless — and mismatched filters are
the single most common reason a dashboard and a query disagree. Capture it even
when nobody asked.
## The flow
1. **Resolve the target.** A dashboard name + URL from the caller, from the context
repo's `domains/*/domain.yaml` `dashboards:` entry, or by asking the user for
the URL. One dashboard per run. If a saved URL, landing page, or bounded
dashboard-catalog lookup does not reveal the named dashboard, stop searching
and use the target-not-found recovery in `references/browser-contract.md`:
ask the user to open that exact dashboard in the connected, already signed-in
local browser and paste its normal address-bar URL, then navigate directly to
it and retry once. Never ask for credentials or a tokenized share link.
2. **Attach the browser** per `references/browser-contract.md`. If no browser
binding is discoverable, run that doc's mode-aware no-binding preflight
instead of failing or repeatedly offering installation. If the page lands
on a login wall, do the credential handoff (fences above). If the direct-URL
retry still cannot reach the named dashboard, report it as unavailable and,
when called from context-interview Stage 5, return control for a human read;
do not resume exploratory browsing or repeat the URL request.
3. **Playbook check.** If the context repo has a learned playbook for this
dashboard (`evals/playbooks/<dashboard-slug>.md`), replay its `replay:` steps
deterministically. Only when there is no playbook — or a replay step's `expect`
fails — work agentically, guided by the shipped per-tool playbook
(`references/playbooks/<tool>.md`); then write/update the learned playbook so
next time is a replay. Replay is the norm; the model is for first encounters
and drift (`references/playbook-format.md`).
4. **Enumerate widgets, capture filter state, extract values** per the hierarchy
above and the tool playbook.
5. **Emit captures** (`references/capture-format.md`), one file per dashboard:
- in a context repo: `evals/captures/<UTC timestamp>/<dashboard-slug>.capture.json`
(generated artifact, gitignored like `evals/runs/`);
- standalone: `./dashboard-verify-captures/<UTC timestamp>/`.
Report back a compact table: widget · value · tier · filter state. If invoked
from context-interview Stage 5, return the capture path(s) — the interview owns
the analyst's blessing; you never mint seeds yourself.
6. **Reconcile (optional).** When the caller also hands you answers to check —
Stage 5's context-on results, or warehouse numbers the user provides — produce
the per-value diff per `references/reconcile.md`: windows compared before
values, tolerance honest about display rounding, report rendered in-session
and written next to the capture as `reconciliation.md`. Without answers to
compare, stop at step 5 — never query a warehouse from this skill.
## Reference files (load per step, not all up front)
- `references/browser-contract.md` — the 7 abstract verbs, bindings, completion
conditions. Load at step 2.
- `references/playbook-format.md` — playbook anatomy, replay semantics, drift
protocol. Load at step 3.
- `references/playbooks/<tool>.md` — per-BI-tool knowledge (currently: `plotly.md`).
Load when the tool is identified.
- `references/capture-format.md` — the capture file shape. Load at step 5.
- `references/reconcile.md` — the capture-vs-answers diff and report. Load only
at step 6, when there are answers to compare.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!