Skip to content

Loops · data-analytics

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
npx skilldrop-cli install --loop measure

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:

npx skilldrop-cli install --loop --pack data-analytics

The loop alone, without its stage skills (each stage falls back to an inline version):

npx skilldrop-cli install --loop measure --no-skills

In Claude Code, the data-analytics plugin includes it:

/plugin install data-analytics@skilldrop

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

#StageWhat it doesSkillsGate
1spec
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—
2review
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-reviewG9 — a review panel decides
READY · NOT READY · BLOCKED
3sign-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-critiqueG9.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

  1. Spec the dashboard. Run dashboard-spec with the audience, the decision and the charts people asked for. Save dashboard-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 marked undefined. Write it down as the person states it, or leave it as a question for the metric's owner. Don't define it yourself.
  2. 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 BLOCKED for that metric, not skipped.
  3. Review every query at G9. Run sql-review on 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.
  4. Map the reviews to G9's verdict:
    • every query SHIP IT or SHIP WITH NOTES → READY. The notes travel to the owner at G9.1.
    • any query BLOCK or FIX FIRST → NOT READY. Return to spec. 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.
  5. Critique, then put each definition to its owner at G9.1. Run doc-critique on 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.

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

Anti-patterns to avoid

Source

LOOP.md · loop.json on GitHub · Why loops