measure
Take a dashboard request to a spec a BI developer can build on numbers people can defend: the decision it serves and one chart per question, every metric defined, the SQL behind each metric reviewed for the bugs that quietly inflate a figure, and the metric owner's sign-off on each definition before anything is built.
- Use it when
- Use when the user says "we need a dashboard for…", "run the measure loop", "make sure these numbers are right before the BI team builds it", or two teams disagree about a number that is about to go on a dashboard.
- Comes with
- The data-analytics pack
- Where you decide
- Review (G9): a review panel decides
- Sign-off (G9.1): you decide
- Size
- 3 stages · 2 gates · up to 3 revision rounds
Installs the loop as an invokable skill, plus every skill its stages run.
Other ways to install
Every loop in its pack, with their skills:
The loop alone, without its stage skills (each stage falls back to an inline version):
In Claude Code, the data-analytics plugin includes it:
The loop
%%{init: {'theme':'base','themeVariables':{'fontFamily':'Segoe UI, Helvetica, Arial','fontSize':'15px','lineColor':'#9AA5B1'},'flowchart':{'curve':'basis','rankSpacing':70,'nodeSpacing':50,'padding':16}}}%%
flowchart LR
classDef gen fill:#E8F0FE,stroke:#4C7DF0,stroke-width:1.5px,color:#1A3A8F;
classDef review fill:#FDEAEA,stroke:#E05B5B,stroke-width:1.5px,color:#8A1F1F;
classDef gate fill:#FBE3A2,stroke:#D9971E,stroke-width:2px,color:#6B4500,font-weight:bold;
classDef ship fill:#E6F7EC,stroke:#34A853,stroke-width:2px,color:#0F6B33,font-weight:bold;
spec["spec<br/><small>dashboard-spec</small>"]:::gen
review["review<br/><small>sql-review</small>"]:::review
sign-off["sign-off<br/><small>doc-critique</small>"]:::gate
G9{"G9 · review"}:::gate
G9_1{"G9.1 · human"}:::gate
DONE(["complete"]):::ship
spec --> review
review --> G9
G9 == "READY" ==> sign-off
sign-off --> G9_1
G9_1 == "PROCEED" ==> DONE
G9 -- "NOT READY (max 3)" --> spec
G9_1 -- "REVISE (max 3)" --> spec
Stages
| # | Stage | What it does | Skills | Gate |
|---|---|---|---|---|
| 1 | spec generate | Pin the audience and the decision, keep only the charts that serve it, and name the defined metric behind each, flagging any metric nobody has defined. | dashboard-spec | — |
| 2 | review verify | Review the query behind every metric that survived the cut list, correctness before performance, with corrected SQL and the queries that prove the fix. | sql-review | G9 — a review panel decides READY · NOT READY · BLOCKED |
| 3 | sign-off gate | Each metric's owner confirms its definition and the number its reviewed query now produces. The critique informs that decision; it does not make it. | doc-critique | G9.1 — you decide PROCEED · PROCEED WITH CONDITIONS · REVISE · BLOCKED |
One dashboard, from the request to a spec whose numbers have been checked. A wrong number on a dashboard does more damage than a wrong number in a query, because the chart makes it look settled. So the loop reviews the SQL before anything is built, and it only reviews the queries that matter. The spec runs first and cuts the charts that serve no decision, so no time goes on queries for charts that won't exist. Last, each metric's owner confirms the definition, because a query can be correct and still compute the wrong thing.
How to run this loop
- Spec the dashboard. Run
dashboard-specwith the audience, the decision and the charts people asked for. Savedashboard-spec.md. Its cut list removes charts before any SQL is read. Every surviving chart names its metric by defined name, grain, window and comparison. A metric with no written definition is markedundefined. Write it down as the person states it, or leave it as a question for the metric's owner. Don't define it yourself. - Collect one query per surviving metric. The person supplies the SQL, the dbt model or the view that computes each one, with the engine and what one output row represents. Where none exists yet, draft one from the definition and the schema they supply, and mark it as a draft. A metric with no query and no schema to draft from is
BLOCKEDfor that metric, not skipped. - Review every query at G9. Run
sql-reviewon each, one at a time. Collect each review's verdict line, findings, corrected query and verification queries. Ask the person to run the verification queries, or run them yourself when you have access. - Map the reviews to G9's verdict:
- every query
SHIP ITorSHIP WITH NOTES→READY. The notes travel to the owner at G9.1. - any query
BLOCKorFIX FIRST→NOT READY. Return tospec. The corrected query becomes that metric's source in the spec, and its verification results go beside it. The fix changes the number the chart will show, sometimes by a lot, so its target, comparison and any threshold the decision uses need checking against it. Then review the corrected query again. Each return counts against the cap.
- every query
- Critique, then put each definition to its owner at G9.1. Run
doc-critiqueon the spec. Then show each metric's owner:- the definition in words;
- the reviewed query;
- what changed and by how much, from the verification queries;
- every assumption the review made about keys, time zones and exclusions. The owner confirms that this is what the business means by the metric. The BI developer builds only from a spec that has passed G9.1.
Verdicts
Defined in contracts/terminals.json.
- G9 emits
READY,NOT READYorBLOCKED(no query text, or no engine to review it against). - G9.1 emits:
PROCEED: every metric's owner confirmed its definition and number.PROCEED WITH CONDITIONS: confirmed, with named items that travel to the build, such as aSHIP WITH NOTEScaveat shown on the chart, or a data gap with its date.REVISE: an owner says the definition is wrong (the wrong time zone, an exclusion missing, the wrong grain). Return tospecwith their definition.BLOCKED: anundefinedmetric has no owner to define it, or an owner is unavailable.
An undefined metric can't pass G9.1. Its owner defines it, or the chart moves to the cut list with the reason.
Non-interactive runs
With no person present, G9.1 cannot be satisfied. Run spec and review, deliver the spec with each query's review, and emit BLOCKED: need each metric owner's confirmation of <every metric on the spec>. A metric whose query passed G9 still needs its owner: the review says the SQL computes the definition, not that the definition is right. Never confirm a definition on an owner's behalf, and never fill an undefined metric with a plausible one: two teams will each read it as theirs.
When a stage's skill is not installed
Name the missing skill and skilldrop as its source, do the minimal inline version of that stage, and record that it ran degraded. A degraded review still checks every join for fan-out, every filter for NULLs and every date boundary for its time zone, in that order, because those are the bugs that change the number. Never skip G9.1.
Run log (opt-in)
Only when the environment variable SKILLDROP_LOOP_LOG is set to a file path: after each gate's verdict, append one JSON line to that file. Nothing else is recorded, and nothing is sent anywhere. When the variable isn't set, or you can't read it, skip this section.
printf '%s\n' '{"ts":"2026-10-19T14:03:00Z","loop":"measure","stage":"review","gate":"G9","verdict":"NOT READY","round":1}' >> "$SKILLDROP_LOOP_LOG"
Fields: ts (UTC), loop, stage, gate (null for a stage without one), verdict (the gate's exact word), and round (1 for the first attempt, counting returns under the cap). The log holds verdicts only: never a query, a table name or a figure. skilldrop loop-stats summarises the file.
Quality bar
- The cut list runs before any SQL is read. Only the queries behind charts that serve the decision get reviewed.
- Every surviving chart's metric has a definition and a reviewed query.
undefinedand unreviewed are build blockers, not notes. - A fixed query goes back through the spec. The corrected number can move a target or a threshold, and the spec is where those live.
- The owner sees the change in the number. The verification queries' before and after go to G9.1, not only the corrected SQL.
- The metric's owner signs the definition. A correct query for the wrong definition is still the wrong number.
Anti-patterns to avoid
- ❌ Reviewing every query in the BI tool. Queries for charts the spec cut cost review time and add nothing.
- ❌ Defining an
undefinedmetric yourself to finish the spec. That settles a disagreement between two teams by picking one at random. - ❌ Shipping the corrected SQL without telling the owner the number moved. A revenue figure that drops a third overnight with no explanation gets reverted.
- ❌ Passing G9 on a clean lint. The lint can't see join fan-out; the review reads the joins.
- ❌ Treating
SHIP WITH NOTESasSHIP IT. The notes go to the owner and, where they change how the chart reads, onto the chart. - ❌ Building before G9.1. A dashboard built on an unconfirmed definition is the one the two teams will argue about.