sanity-radar
GitHubSanity Studio 仓库健康仪表盘,用于监控性能趋势、版本发布及回归分析。支持通过 GROQ 查询数据,提供趋势图、发布记录、二分查找等工具,辅助定位代码变更对构建性能的影响。
Trigger Scenarios
Install
npx skills add sanity-io/sanity --skill sanity-radar -g -y
SKILL.md
Frontmatter
{
"name": "sanity-radar",
"description": "Studio Radar, the repo-health dashboard at radar.sanity.dev (source in dev\/radar) — what each tool shows, where the data lives, how to query benchRun \/ gitCommit \/ gitTag \/ bisectSession documents with GROQ (read token required), and how to run, test and change the dashboard code. Use when asked about studio performance trends, drift flags, the bench dataset, release markers, the Bisect or Comparisons tools, \"what did the daily bench say\", or when editing anything under dev\/radar."
}
Studio Radar
Radar answers repo-health questions without opening CI logs: is studio performance drifting on
main, what did a run look like, which commit broke it, what shipped when. It is a Sanity Studio
(dev/radar, deployed at https://radar.sanity.dev) reading a dataset that CI writes. The design
record is dev/radar/SPEC.md — read the section for the view you touch, and update it when
behavior changes. For regression hunting use sanity-radar-investigate; for producing the data
use sanity-bench.
Tools (URL path = tool name)
| Tool | Path | Shows |
|---|---|---|
| Trends | /trends |
Small multiples per scenario·metric on main: p50 line, p75–p90 band, host calibration (dotted), drift baseline overlay (dashed before / solid after), release ticks. Default view. |
| Releases | /releases |
Every synced v* tag: dist-tags, downloads, changelog links, regressions bisect attributed to it. |
| Bisect | /bisect |
Guided first-parent bisect over gitCommit using each commit's test-studio preview build. |
| Diagnostics | /diagnostics |
Paste a studio diagnostics JSON, render it (in-studio twin of dev/studio-diagnostics-viewer). |
| Structure | /structure |
Raw documents. |
| Comparisons | /comparisons |
Stored mode: "ab" runs from A/B dispatches, verdict per metric. |
Trends URL state, all shareable: ?range=30|90|all, ?branches=main,<branch>,
?layers=-band,-calibration (hide layers), ?tab=<metric group>, ?max=<series key> (one chart maximized).
Releases: ?path=<test-studio path> (the path field under the header) opens every release's Test Studio link at that path.
Clicking a point opens the run popover: value, percentiles, host, release context, links, and
under "Suspect a regression?" the GitHub compare of the gap, Copy A/B vs previous run (the
gh workflow run bench.yml … ab_from/ab_to command) and Copy investigation prompt (a
paste-ready brief for an agent).
The data
Project mhfozd0z, dataset bench, private: reads need a token with access to the project
(your CLI token via sanity debug --secrets, or a viewer token from sanity.io/manage; the deployed
studio authenticates through your login). Writes need RADAR_SANITY_WRITE_TOKEN, which only CI
has; never write from a laptop. Ids are built in one place, @repo/utils/radar-ids: lowercase,
dashes, no dots (a dot makes an id a path like drafts.x, which the API scopes differently).
| Type | Written by | Id | Key fields |
|---|---|---|---|
benchRun |
bench store in .github/workflows/bench.yml |
bench-run-<sha>-<run id> / bench-run-pr-<n> |
mode (absolute = time-series point, ab = investigation record), trigger (cron/release/backfill/dispatch/pr; absent = cron), git{sha, branch, committedAt, mergeBaseSha, prNumber}, runner{calibrationMs, cpuModel, runId…}, scenarios[]{scenario, mode, runner{calibrationMs, cpuModel}, metrics[]{label, unit, experiment/reference{summary{median,p75,p90}}, comparison{diff,lo,hi,verdict}}, resources, soak}, bundle{initialJsBytes, totalJsBytes} |
gitCommit |
sync-git-metrics.yml (every push to main) |
git-commit-<sha> |
sha, parentSha (first-parent chain), committedAt, subject, prNumber, author, testStudioUrl |
gitTag |
same, releases and a daily npm floor | git-tag-v6-10-1 |
tag, sha, taggedAt, major/minor/patch/prerelease, npm{distTags, weeklyDownloads, publishedAt, deprecated} |
bisectSession |
Bisect tool (user-owned, liveEdit) | bisect-session-<uuid> |
description (the issue), refines (weak ref to the session it narrows down — a chain counts as one regression), good/bad{sha}, releasesOnly, reproPath (test-studio path appended to every preview URL), marks[], result{firstBadSha, suspectShas, regression, note, severity (minor/major/critical), linearIssue, fixedIn} |
releaseLine |
Releases tool (user-owned, liveEdit) | release-line-v<major> |
major, eolMarkedAt, eolMarkedBy, note — existence means the major is end of life; reinstating deletes it |
driftAck |
Trends drift feed (user-owned) | drift-ack-<slug of metricKey:branch> |
state (silenced/snoozed/fixed), baselineValue, until |
Rules that every consumer follows (the dashboard's queries are the reference, tools/*/data.ts):
- Project, never fetch
sessions. Documents carry per-session sample arrays; a bare*[_type == "benchRun"]is megabytes. - Time series =
mode == "absolute", ordered bycoalesce(git.committedAt, startedAt)(backfill runs have a historicalcommittedAtand a recentstartedAt). PR-branch runs share the type — filtergit.branch == "main"for the main line. - Same-sha runs are one point. CI re-measures commits; the charts merge them by median.
- Absolute numbers are host-relative. Always read
runner.calibrationMs(higher = slower host) andrunner.cpuModelnext to any value; only A/Bcomparison.verdicts are host-independent. git.shais the join key togitCommit;benchRun.git.commitis a weak reference that dangles for PR-branch runs.
Ready-made queries: references/groq-recipes.md.
Working on the dashboard
pnpm radar # sanity dev on http://localhost:3399 (needs a Sanity login; reads live data)
pnpm vitest run --project=radar # pure modules: trend math, drift, bisect, git parsers
pnpm build:radar # production build (turbo)
pnpm --filter radar sync-git -- --dry-run # preview the git-history sync (needs GITHUB_TOKEN)
- Realtime everywhere:
useDocumentStore().listenQuery+useObservable, never one-shotclient.fetch. New cron runs must appear without a reload. - Charts are visx primitives (
@visx/scale|shape|group|axis|responsive) inside@sanity/uilayout; no chart library. - Debug data sources (dev server only, toolbar picker;
tools/trends/debugData.ts) render steady/drift/step/host-correlated/sparse/empty datasets and synthetic release tags — use them to exercise every encoding layer and the drift feed without live data. UI is verified through these, not unit tests. - One source of truth for "enough to care": drift thresholds import from
perf/bench/stats/gate.ts. Don't invent a second statistic; SPEC.md records the baselines that were tried and rejected (step, weekday-matched) and why. - Honesty constraint: any surface showing a ms value must make host calibration visible (
CALIBRATION_EXPLAINERis the one shared wording). - Studio conventions from
AGENTS.mdapply (noforwardRef,use-effect-event, no inline Translate components).
Operations
- Daily main run:
bench.ymlcron 05:00 UTC; git sync 05:30. A missed day is repaired with a backfill dispatch (seesanity-bench). A gap in commits:gh workflow run sync-git-metrics.yml -f backfill=true. - Release markers only appear for tags whose commit has a
gitCommitdocument (main-only by construction); a maintenance release cut from a branch is correctly absent. - Cron failures alert the CI Slack channel; a hole in the series with no alert means the store step was skipped, not that the run failed.
Version History
- 584242a Current 2026-09-23 01:00


