@graphit/cli 0.1.43 → 0.1.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/dist/commands/query.js +71 -14
- package/dist/commands/query.js.map +1 -1
- package/dist/output/styled.d.ts +5 -0
- package/dist/output/styled.js +9 -0
- package/dist/output/styled.js.map +1 -1
- package/package.json +1 -1
- package/skills/graphit/SKILL.md +10 -3
- package/skills/graphit/VERSION.json +1 -1
- package/skills/graphit/cursor/graphit-chart-patterns.mdc +3 -0
- package/skills/graphit/cursor/graphit-data-sources.mdc +13 -0
- package/skills/graphit/cursor/graphit-filters.mdc +3 -2
- package/skills/graphit/cursor/graphit-kb-exploration.mdc +2 -0
- package/skills/graphit/cursor/graphit-sql-reference.mdc +3 -1
- package/skills/graphit/graphit.mdc +6 -3
- package/skills/graphit/references/chart-patterns.md +3 -0
- package/skills/graphit/references/data-sources.md +13 -0
- package/skills/graphit/references/filters.md +5 -3
- package/skills/graphit/references/governance.md +9 -2
- package/skills/graphit/references/kb-discovery.md +2 -0
- package/skills/graphit/references/sql-reference.md +1 -1
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "Graphit CLI plugin for AI coding assistants",
|
|
10
|
-
"version": "0.1.
|
|
10
|
+
"version": "0.1.51"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"source": {
|
|
17
17
|
"source": "npm",
|
|
18
18
|
"package": "@graphit/cli",
|
|
19
|
-
"version": "0.1.
|
|
19
|
+
"version": "0.1.51"
|
|
20
20
|
},
|
|
21
|
-
"version": "0.1.
|
|
21
|
+
"version": "0.1.51",
|
|
22
22
|
"category": "data-visualization",
|
|
23
23
|
"tags": [
|
|
24
24
|
"bi",
|
package/dist/commands/query.js
CHANGED
|
@@ -1,7 +1,39 @@
|
|
|
1
1
|
import { apiClient } from "../api/client.js";
|
|
2
2
|
import { output, errorOutput, getOutputFormat } from "../output/format.js";
|
|
3
|
-
import { styledIntro, styledOutro, styledSuccess, styledNote, styledMessage, styledWarn, styledTable, formatBold, formatDim, formatGreen, formatSeparator, } from "../output/styled.js";
|
|
3
|
+
import { styledIntro, styledOutro, styledSuccess, styledNote, styledMessage, styledWarn, styledTable, styledConfirm, formatBold, formatDim, formatGreen, formatSeparator, } from "../output/styled.js";
|
|
4
4
|
import chalk from "chalk";
|
|
5
|
+
function adhocGateDetail(err) {
|
|
6
|
+
const e = err;
|
|
7
|
+
const detail = e?.detail;
|
|
8
|
+
if (e?.status === 422 && detail && typeof detail === "object") {
|
|
9
|
+
return detail;
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
async function promptAdhocGate(gate, fmt) {
|
|
14
|
+
const reason = gate.reason || "This ad-hoc measure query runs on a governed data source.";
|
|
15
|
+
const suggestion = gate.suggested_action || "Use {{metric:NAME}} references, or re-run with --approve-adhoc.";
|
|
16
|
+
const measures = gate.unmatched_expressions?.length ? gate.unmatched_expressions.join(", ") : "";
|
|
17
|
+
const interactive = (process.stdout.isTTY ?? false) && fmt === "styled";
|
|
18
|
+
// Agent / CI / piped / json: print a clear rejection; the caller exits non-zero
|
|
19
|
+
// so the driving agent rewrites with {{metric:X}} or asks its user.
|
|
20
|
+
if (!interactive) {
|
|
21
|
+
console.log(`Query blocked: ${reason}`);
|
|
22
|
+
if (measures)
|
|
23
|
+
console.log(`Measures without a KB metric: ${measures}`);
|
|
24
|
+
console.log(suggestion);
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
styledWarn(reason);
|
|
28
|
+
if (measures)
|
|
29
|
+
styledMessage(formatDim(`Measures without a KB metric: ${measures}`));
|
|
30
|
+
styledMessage(suggestion);
|
|
31
|
+
return styledConfirm("Run this ad-hoc measure query against the governed source?", {
|
|
32
|
+
active: "Yes, run ad-hoc",
|
|
33
|
+
inactive: "No, cancel",
|
|
34
|
+
initialValue: false,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
5
37
|
export function registerQueryCommands(program) {
|
|
6
38
|
program
|
|
7
39
|
.command("query <sql>")
|
|
@@ -12,18 +44,21 @@ export function registerQueryCommands(program) {
|
|
|
12
44
|
.option("--limit <n>", "Max rows returned", "50")
|
|
13
45
|
.option("--override-rules <names...>", "Governance rule names to override")
|
|
14
46
|
.option("--verbose", "Show expanded SQL after macro resolution")
|
|
47
|
+
.option("--approve-adhoc", "Approve running an ad-hoc measure query on a governed source")
|
|
15
48
|
.action(async function (sql) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
49
|
+
const cmd = this;
|
|
50
|
+
const opts = cmd.opts();
|
|
51
|
+
const fmt = getOutputFormat(cmd);
|
|
52
|
+
const makeBody = (approveAdhoc) => ({
|
|
53
|
+
sql,
|
|
54
|
+
data_source_id: opts.ds || null,
|
|
55
|
+
warehouse: !!opts.warehouse,
|
|
56
|
+
connection_id: opts.connection || null,
|
|
57
|
+
limit: parseInt(opts.limit, 10),
|
|
58
|
+
override_rules: opts.overrideRules || null,
|
|
59
|
+
approve_adhoc: approveAdhoc,
|
|
60
|
+
});
|
|
61
|
+
const renderResult = (resp) => {
|
|
27
62
|
if (fmt === "styled") {
|
|
28
63
|
styledIntro("graphit query");
|
|
29
64
|
styledSuccess("Query executed");
|
|
@@ -65,7 +100,7 @@ export function registerQueryCommands(program) {
|
|
|
65
100
|
if (opts.verbose && resp.governed_sql) {
|
|
66
101
|
console.log(`\nExpanded SQL:\n${resp.governed_sql}\n`);
|
|
67
102
|
}
|
|
68
|
-
output(
|
|
103
|
+
output(cmd, resp);
|
|
69
104
|
const prov = resp.provenance;
|
|
70
105
|
if (prov) {
|
|
71
106
|
const tierIcons = { governed: "[governed]", verified: "[verified]", ad_hoc: "[ad-hoc]" };
|
|
@@ -90,9 +125,31 @@ export function registerQueryCommands(program) {
|
|
|
90
125
|
}
|
|
91
126
|
}
|
|
92
127
|
}
|
|
128
|
+
};
|
|
129
|
+
try {
|
|
130
|
+
const resp = await apiClient.post("/api/v1/cli/query", makeBody(!!opts.approveAdhoc));
|
|
131
|
+
renderResult(resp);
|
|
93
132
|
}
|
|
94
133
|
catch (err) {
|
|
95
|
-
|
|
134
|
+
// Feature #616: a 422 is the ad-hoc measure gate - prompt (TTY) or print
|
|
135
|
+
// a rejection (agent/CI), then re-run with approval if the user said yes.
|
|
136
|
+
const gate = adhocGateDetail(err);
|
|
137
|
+
if (!gate) {
|
|
138
|
+
errorOutput(err);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const approved = await promptAdhocGate(gate, fmt);
|
|
142
|
+
if (!approved) {
|
|
143
|
+
process.exitCode = 1;
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const resp = await apiClient.post("/api/v1/cli/query", makeBody(true));
|
|
148
|
+
renderResult(resp);
|
|
149
|
+
}
|
|
150
|
+
catch (err2) {
|
|
151
|
+
errorOutput(err2);
|
|
152
|
+
}
|
|
96
153
|
}
|
|
97
154
|
});
|
|
98
155
|
const metadata = program.command("metadata").description("Snowflake metadata");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EACL,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAC9E,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"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"}
|
package/dist/output/styled.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ export declare function styledSuccess(message: string): void;
|
|
|
6
6
|
export declare function styledInfo(message: string): void;
|
|
7
7
|
export declare function styledMessage(content: string): void;
|
|
8
8
|
export declare function styledNote(content: string, title: string): void;
|
|
9
|
+
export declare function styledConfirm(message: string, opts?: {
|
|
10
|
+
active?: string;
|
|
11
|
+
inactive?: string;
|
|
12
|
+
initialValue?: boolean;
|
|
13
|
+
}): Promise<boolean>;
|
|
9
14
|
export declare function styledTable(headers: string[], rows: (string | number | null | undefined)[][], opts?: {
|
|
10
15
|
rightAlign?: number[];
|
|
11
16
|
}): string;
|
package/dist/output/styled.js
CHANGED
|
@@ -24,6 +24,15 @@ export function styledMessage(content) {
|
|
|
24
24
|
export function styledNote(content, title) {
|
|
25
25
|
p.note(content, title);
|
|
26
26
|
}
|
|
27
|
+
export async function styledConfirm(message, opts) {
|
|
28
|
+
const answer = await p.confirm({
|
|
29
|
+
message,
|
|
30
|
+
active: opts?.active ?? "Yes",
|
|
31
|
+
inactive: opts?.inactive ?? "No",
|
|
32
|
+
initialValue: opts?.initialValue ?? false,
|
|
33
|
+
});
|
|
34
|
+
return !p.isCancel(answer) && answer === true;
|
|
35
|
+
}
|
|
27
36
|
export function styledTable(headers, rows, opts) {
|
|
28
37
|
const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => String(r[i] ?? "").length)));
|
|
29
38
|
const headerLine = headers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styled.js","sourceRoot":"","sources":["../../src/output/styled.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"styled.js","sourceRoot":"","sources":["../../src/output/styled.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/skills/graphit/SKILL.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
skill_version: "0.1.
|
|
2
|
+
skill_version: "0.1.51"
|
|
3
3
|
name: graphit
|
|
4
4
|
description: >
|
|
5
5
|
Build HTML dashboards with Graphit. KB-aware queries, entity wrapping, cached data sources.
|
|
@@ -281,6 +281,7 @@ For bulk creation (10+ assets), use `--skip-validate` to avoid per-asset latency
|
|
|
281
281
|
| `graphit query "<sql>" --ds <id>` | Query cached data source (~100ms) |
|
|
282
282
|
| `graphit query "<sql>" --ds <id> --override-rules RULE1 RULE2` | Query with governance rule overrides |
|
|
283
283
|
| `graphit query "<sql>" --ds <id> --verbose` | Show expanded SQL and trust tier |
|
|
284
|
+
| `graphit query "<sql>" --ds <id> --approve-adhoc` | Approve running an ad-hoc measure query on a governed source |
|
|
284
285
|
| `graphit query "<sql>" --warehouse --connection <id>` | Query live Snowflake (~10s) |
|
|
285
286
|
| `graphit governance status` | Show governance mode and conformance stats |
|
|
286
287
|
| `graphit governance set <mode>` | Set governance mode (observe/warn/strict) |
|
|
@@ -344,6 +345,10 @@ graphit query "SELECT {{metric:CPI}} as cpi FROM MARKETING_UA_DS" --ds ds_abc123
|
|
|
344
345
|
|
|
345
346
|
Queries using reference syntax produce **governed** trust tier. Inline formulas produce **ad-hoc** tier, which may be blocked in strict mode. Use `--override-rules RULE_NAME` to bypass specific enforceable rules when the user explicitly requests it.
|
|
346
347
|
|
|
348
|
+
**Ad-hoc measure gate.** On a governed data source:
|
|
349
|
+
- **warn mode** asks before running an ad-hoc *measure* query - one that computes a business measure (an aggregate or a `GROUP BY` producing a metric) without `{{metric:NAME}}` references. The command is rejected; first rewrite using `{{metric:NAME}}` / `{{dim:NAME}}` references (search the KB for a match). If the user genuinely needs the raw run, ask them, then re-run the exact command with `--approve-adhoc`. Plain exploration runs free - `SELECT *`, raw columns, `COUNT(*)` row-count peeks, and `DISTINCT` value lists.
|
|
350
|
+
- **strict mode** hard-blocks *all* ad-hoc queries (including exploration) - rewrite with `{{metric:NAME}}` references or stop. `--approve-adhoc` does not apply.
|
|
351
|
+
|
|
347
352
|
Trust tiers: **governed** (used KB references), **verified** (raw SQL matches KB definitions), **ad-hoc** (inline formulas). Badges appear on dashboard graphs and canvas entities.
|
|
348
353
|
|
|
349
354
|
Use reference syntax in `graphit.resolve()` calls too - the server expands them at query time:
|
|
@@ -410,7 +415,7 @@ The iframe also provides optional convenience helpers if you want quick standard
|
|
|
410
415
|
| Helper | Usage |
|
|
411
416
|
|---|---|
|
|
412
417
|
| `graphit.chart(el, {type, data, x, y, ...})` | Bar, line, area, donut, scatter, stacked-bar, heatmap, funnel, gauge, sparkline |
|
|
413
|
-
| `graphit.table(el, {data, columns?, maxRows?})` | Styled HTML table. `columns` is a `string[]` of data keys (NOT objects), defaults to `Object.keys(data[0])` |
|
|
418
|
+
| `graphit.table(el, {data, columns?, maxRows?, columnFormats?})` | Styled HTML table. `columns` is a `string[]` of data keys (NOT objects), defaults to `Object.keys(data[0])`. `columnFormats` maps column name -> `"currency"`/`"percent"`/`"number"` for per-column number formatting (same formats and no-scaling percent rule as chart `valueFormat`); unlisted columns render raw |
|
|
414
419
|
| `graphit.kpi(el, {value, label?, format?})` | KPI card with optional delta |
|
|
415
420
|
| `graphit.presentation(el)` | Full-screen slide deck. Returns builder: `.slide({bg, layout, html})` then `.start()`. See `presentations.md` |
|
|
416
421
|
| `graphit.filter(id, {label, field?, default?})` | Headless filter registration (renders nothing). Returns handle: `{get, set, subscribe}`. See `filters.md` |
|
|
@@ -427,6 +432,8 @@ These are shortcuts, not requirements. Use them when a standard chart is all you
|
|
|
427
432
|
|
|
428
433
|
`graphit.kpi` config: `value`, `label`, `format` (`"currency"` | `"percent"` | `"number"`), `compareValue`, `compareLabel`.
|
|
429
434
|
|
|
435
|
+
**Percent does NOT scale.** `"percent"` only appends a `%` sign - it does not multiply by 100. A 0-1 ratio (retention, conversion, share, churn) renders as `0.42%`, not `42%`. Multiply ratios by 100 in SQL so the value is already 0-100, e.g. `SUM(retained) * 100.0 / NULLIF(SUM(cohort), 0) AS retention_pct` charted with `valueFormat: "percent"` -> "42%". Charts use `valueFormat`; `kpi`/`gauge`/`filter` use `format`.
|
|
436
|
+
|
|
430
437
|
### Canonical pattern - entity with live data
|
|
431
438
|
|
|
432
439
|
```html
|
|
@@ -475,4 +482,4 @@ Detailed knowledge lives in `references/`. Consult the relevant file when you ne
|
|
|
475
482
|
| `governance.md` | Query governance. Reference syntax, trust tiers, enforceable rules, override flow, governance commands. |
|
|
476
483
|
| `presentations.md` | Slide deck presentations. Builder API, layouts (center/split/full), backgrounds, navigation, live data inside slides. |
|
|
477
484
|
| `filters.md` | Headless filters and parameters. `graphit.filter()`/`graphit.param()` registration (zero DOM), `graphit.bind()` reactive binding, `:name` safe param syntax, saved views. |
|
|
478
|
-
| `data-sources.md` | Building performant, cache-friendly data sources. Why source shape decides dashboard speed; pre-aggregate to query grain, narrow columns, keep high-cardinality dimensions out of the base. |
|
|
485
|
+
| `data-sources.md` | Building performant, cache-friendly data sources. Why source shape decides dashboard speed; pre-aggregate to query grain, narrow columns, keep high-cardinality dimensions out of the base; slow-shape signals to recognize before building. |
|
|
@@ -47,6 +47,9 @@ Config: `value`, `min` (default 0), `max` (default 100), `format`, `label`, `col
|
|
|
47
47
|
Inline SVG polyline for KPI cards or table cells. No axes, no grid - just trend shape. Single data point renders as a dot.
|
|
48
48
|
Config: `y`, `width` (default 120), `height` (default 32), `label`, `showValue` (default true), `valueFormat`.
|
|
49
49
|
|
|
50
|
+
## Value formatting
|
|
51
|
+
`valueFormat` (charts), `format` (`kpi`/`gauge`), and `columnFormats` (`graphit.table`, mapping column name -> format) all take `"currency"`, `"percent"`, or `"number"`. **`"percent"` only appends `%` - it does NOT multiply by 100.** A 0-1 ratio renders as `0.42%`, not `42%`; multiply ratios/rates by 100 in SQL (`* 100.0 ... AS x_pct`) so the value is already 0-100. Table columns without a `columnFormats` entry render raw.
|
|
52
|
+
|
|
50
53
|
## Color tokens
|
|
51
54
|
|
|
52
55
|
| Token | Usage |
|
|
@@ -30,6 +30,19 @@ When you change a dashboard filter (e.g. switch country), Graphit answers from a
|
|
|
30
30
|
|
|
31
31
|
Build the source small and pre-aggregated, and dashboards on it stay fast and filterable.
|
|
32
32
|
|
|
33
|
+
## Slow-shape signals to watch for
|
|
34
|
+
|
|
35
|
+
Before creating a source, check your own SQL for these. Each is a reason to offer the user a faster shape, never to refuse:
|
|
36
|
+
|
|
37
|
+
| Signal | What it looks like | Faster option |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| Raw passthrough | No `GROUP BY` / no aggregate - one row per raw event | Pre-aggregate to the grain the dashboards chart |
|
|
40
|
+
| Very wide | Far more columns than dashboards use (e.g. `SELECT *`) | Select only the columns dashboards need |
|
|
41
|
+
| High-cardinality grain | A dimension with thousands of distinct values (ad / campaign / user ids) | Keep it out of the base; build a separate drill-down source |
|
|
42
|
+
| Large + monolithic | A big source whose typical query still scans most rows | Pre-aggregate and narrow so typical queries touch a few thousand rows |
|
|
43
|
+
|
|
44
|
+
A wide or raw source is sometimes the right call - row-level drill-down/export, columns genuinely all used, or a staging source to reshape later. Note the trade-off, then build whichever the user chooses.
|
|
45
|
+
|
|
33
46
|
## Creating data sources
|
|
34
47
|
|
|
35
48
|
`graphit ds create` auto-chains: create -> poll until ready -> scan schema -> print verification link. The user must visit the verification link on the platform to review AI-generated column descriptions and activate the DS for KB use.
|
|
@@ -53,7 +53,7 @@ graphit.bind(document.getElementById('chart'), {
|
|
|
53
53
|
```js
|
|
54
54
|
const dr = graphit.dateRange('date_range', { label: 'Date Range', default: 'last_30_days' });
|
|
55
55
|
// dr.get() -> {preset,start,end}; dr.set('this_month'); dr.setRange(s,e); dr.start/.end/.deps; dr.subscribe(cb)
|
|
56
|
-
// bind with two scalars: WHERE day BETWEEN :
|
|
56
|
+
// bind with two scalars: WHERE day BETWEEN :start_date AND :end_date, params:()=>({start_date:dr.start,end_date:dr.end}), deps:dr.deps
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
Relative presets recompute on reload. Ids: today, yesterday, last_7_days, last_30_days, this_month, last_month, this_quarter, last_quarter, ytd, last_90_days, last_12_months (`graphit.datePresets` / `graphit.datePreset(id)`).
|
|
@@ -80,7 +80,8 @@ For low-cardinality cascades add `preload: true`: loads the distinct cross-produ
|
|
|
80
80
|
Use `:name` placeholders (never string concat):
|
|
81
81
|
- Scalar: `WHERE x = :x` with `{x: 'val'}`
|
|
82
82
|
- Multi-select: `WHERE x IN :xs` with `{xs: ['a','b']}` (expands to `IN ($0,$1)`)
|
|
83
|
-
- Range: `WHERE d BETWEEN :
|
|
83
|
+
- Range: `WHERE d BETWEEN :start_date AND :end_date` with `{start_date: '...', end_date: '...'}`
|
|
84
|
+
- Param names can't be SQL keywords (`from`, `to`, `select`, ...) - template is parsed before binding, so `:from` -> "SQL validation failed". Use `:start_date`/`:end_date`.
|
|
84
85
|
|
|
85
86
|
## Saved Views
|
|
86
87
|
|
|
@@ -39,6 +39,8 @@ All KB assets use UPPER_SNAKE_CASE. The names are auto-sanitized:
|
|
|
39
39
|
| `COUNT_*` | `COUNT_ACTIVE_USERS` | Count metrics |
|
|
40
40
|
| `*_RATE` | `CONVERSION_RATE`, `CHURN_RATE` | Ratios/percentages |
|
|
41
41
|
|
|
42
|
+
> `*_RATE`/ratio columns are usually 0-1 fractions. To chart them as a percent, multiply by 100 in SQL (`* 100.0`) - `"percent"` format appends `%` without scaling.
|
|
43
|
+
|
|
42
44
|
## When to Suggest KB Asset Creation
|
|
43
45
|
|
|
44
46
|
| Signal | Propose |
|
|
@@ -133,7 +133,7 @@ Canvas `graphit.resolve()` queries that follow these shapes serve from a semanti
|
|
|
133
133
|
**Shapes that skip the cache** (fall back to normal execution, still correct):
|
|
134
134
|
- `COUNT(DISTINCT x)`, window functions, HAVING, QUALIFY
|
|
135
135
|
- OR / NOT in WHERE
|
|
136
|
-
- Ratio metrics (`SUM(a)/NULLIF(SUM(b),0)`) - compute client-side or use two resolves
|
|
136
|
+
- Ratio metrics (`SUM(a)/NULLIF(SUM(b),0)`) - compute client-side or use two resolves. To display as a percent, multiply by 100 (`* 100.0 ... AS x_pct`): the `"percent"` format only appends `%`, it does not scale (a 0-1 ratio would show as `0.42%`, not `42%`).
|
|
137
137
|
- `CURRENT_DATE`-relative predicates
|
|
138
138
|
- Top-N with aggregate only in ORDER BY (`SELECT dim FROM t GROUP BY dim ORDER BY SUM(metric) DESC LIMIT N` - no decomposable aggregate in SELECT)
|
|
139
139
|
|
|
@@ -157,6 +157,8 @@ Ground every query in the KB. When using `{{metric:X}}`/`{{dim:X}}` references,
|
|
|
157
157
|
|
|
158
158
|
For inline SQL (no KB refs): show query + results + governance, and suggest the KB reference equivalent to nudge toward governed tier.
|
|
159
159
|
|
|
160
|
+
On a governed DS, an ad-hoc **measure** query (aggregate / `GROUP BY` without `{{metric:X}}`) is gated: warn mode rejects it (rewrite with `{{metric:X}}`, or ask the user and re-run with `--approve-adhoc`); strict hard-blocks all ad-hoc. Exploration (`SELECT *`, `COUNT(*)`, `DISTINCT`) runs free.
|
|
161
|
+
|
|
160
162
|
## Presenting Data Source Results
|
|
161
163
|
|
|
162
164
|
After `graphit ds list`: table with **bold Name**, ID, Rows (right-aligned), Status, Governed. End with DS recommendation for current task.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
skill_version: "0.1.
|
|
2
|
+
skill_version: "0.1.51"
|
|
3
3
|
alwaysApply: false
|
|
4
4
|
description: "Build Graphit HTML dashboards with KB-aware queries, entity wrapping, and cached data sources"
|
|
5
5
|
globs: []
|
|
@@ -224,6 +224,7 @@ You are the rendering layer - format and present every CLI result using markdown
|
|
|
224
224
|
| `graphit query "<sql>" --ds <id>` | Query cached data source (~100ms) |
|
|
225
225
|
| `graphit query "<sql>" --ds <id> --override-rules RULE1 RULE2` | Query with governance rule overrides |
|
|
226
226
|
| `graphit query "<sql>" --ds <id> --verbose` | Show expanded SQL and trust tier |
|
|
227
|
+
| `graphit query "<sql>" --ds <id> --approve-adhoc` | Approve running an ad-hoc measure query on a governed source |
|
|
227
228
|
| `graphit query "<sql>" --warehouse --connection <id>` | Query live Snowflake (~10s) |
|
|
228
229
|
| `graphit governance status` | Show governance mode and conformance stats |
|
|
229
230
|
| `graphit governance set <mode>` | Set governance mode (observe/warn/strict) |
|
|
@@ -266,7 +267,7 @@ graphit governance set warn # change mode (admin)
|
|
|
266
267
|
graphit ds update <id> --governed-mode on # enable governance on DS
|
|
267
268
|
```
|
|
268
269
|
|
|
269
|
-
Reference syntax in `graphit.resolve()` calls works the same way - server expands at query time. Trust tiers: **governed** (KB refs), **verified** (matches KB), **ad-hoc** (inline).
|
|
270
|
+
Reference syntax in `graphit.resolve()` calls works the same way - server expands at query time. Trust tiers: **governed** (KB refs), **verified** (matches KB), **ad-hoc** (inline). On a governed DS, an ad-hoc **measure** query (aggregate / `GROUP BY` without `{{metric:X}}`) is gated: warn mode rejects it (rewrite with `{{metric:X}}`, or ask the user and re-run with `--approve-adhoc`); strict hard-blocks all ad-hoc. Exploration (`SELECT *`, `COUNT(*)`, `DISTINCT`) runs free.
|
|
270
271
|
|
|
271
272
|
---
|
|
272
273
|
|
|
@@ -304,7 +305,7 @@ Add the overlay inside EVERY element you pass as `target:` - and ONLY those elem
|
|
|
304
305
|
| Helper | Usage |
|
|
305
306
|
|---|---|
|
|
306
307
|
| `graphit.chart(el, {type, data, x, y, ...})` | Bar, horizontal-bar, line, area, donut, scatter, stacked-bar, heatmap, funnel, gauge, sparkline |
|
|
307
|
-
| `graphit.table(el, {data, columns?, maxRows?})` | Styled HTML table. `columns` is a `string[]` of data keys (NOT objects) |
|
|
308
|
+
| `graphit.table(el, {data, columns?, maxRows?, columnFormats?})` | Styled HTML table. `columns` is a `string[]` of data keys (NOT objects). `columnFormats` maps column name -> `"currency"`/`"percent"`/`"number"` (same formats and no-scaling percent rule as chart `valueFormat`); unlisted columns render raw |
|
|
308
309
|
| `graphit.kpi(el, {value, label?, format?})` | KPI card with optional delta (`compareValue`, `compareLabel`) |
|
|
309
310
|
| `graphit.presentation(el)` | Full-screen slide deck. Returns builder: `.slide({bg, layout, html})` then `.start()`. See graphit-presentations rule. |
|
|
310
311
|
| `graphit.filter(id, {label, field?, default?})` | Headless filter (zero DOM). Returns `{get, set, subscribe}` handle. See graphit-filters rule. |
|
|
@@ -317,6 +318,8 @@ Add the overlay inside EVERY element you pass as `target:` - and ONLY those elem
|
|
|
317
318
|
|
|
318
319
|
`graphit.chart` types: `"bar"`, `"horizontal-bar"` (alias `"hbar"`), `"line"`, `"area"`, `"donut"` (alias `"pie"`), `"scatter"` (alias `"bubble"`), `"stacked-bar"` (alias `"stacked"`), `"heatmap"`, `"funnel"`, `"gauge"`, `"sparkline"`. Config: `x`, `y`, `series`, `title`, `height` (140-900px), `valueFormat` (`"currency"` | `"percent"` | `"number"`), `colors`. Dual axis (bar/line/area): `y2`, `y2Format`, `y2Label`; `y2` and `series` are mutually exclusive. Scatter adds: `size`, `label`. Gauge: `min`, `max`, `format`. Sparkline: `width`, `showValue`.
|
|
319
320
|
|
|
321
|
+
**Percent does NOT scale.** `"percent"` only appends `%` - it does not multiply by 100. A 0-1 ratio (retention, conversion, share, churn) renders as `0.42%`, not `42%`. Multiply ratios by 100 in SQL (`SUM(retained) * 100.0 / NULLIF(SUM(cohort), 0) AS retention_pct`) then use `valueFormat: "percent"`. Charts use `valueFormat`; `kpi`/`gauge`/`filter` use `format`.
|
|
322
|
+
|
|
320
323
|
### Canonical pattern - entity with live data
|
|
321
324
|
|
|
322
325
|
```html
|
|
@@ -44,6 +44,9 @@ Config: `value`, `min` (default 0), `max` (default 100), `format`, `label`, `col
|
|
|
44
44
|
Inline SVG polyline for KPI cards or table cells. No axes, no grid - just trend shape. Single data point renders as a dot.
|
|
45
45
|
Config: `y`, `width` (default 120), `height` (default 32), `label`, `showValue` (default true), `valueFormat`.
|
|
46
46
|
|
|
47
|
+
## Value formatting
|
|
48
|
+
`valueFormat` (charts), `format` (`kpi`/`gauge`), and `columnFormats` (`graphit.table`, mapping column name -> format) all take `"currency"`, `"percent"`, or `"number"`. **`"percent"` only appends `%` - it does NOT multiply by 100.** A 0-1 ratio renders as `0.42%`, not `42%`; multiply ratios/rates by 100 in SQL (`* 100.0 ... AS x_pct`) so the value is already 0-100. Table columns without a `columnFormats` entry render raw.
|
|
49
|
+
|
|
47
50
|
## Saved templates
|
|
48
51
|
|
|
49
52
|
Templates are reusable chart components saved to the org's KB. At dashboard load, the SDK fetches the org's template bundle and registers them alongside built-in types.
|
|
@@ -24,6 +24,19 @@ When you change a dashboard filter (e.g. switch country), Graphit answers from a
|
|
|
24
24
|
|
|
25
25
|
Build the source small and pre-aggregated, and dashboards on it stay fast and filterable.
|
|
26
26
|
|
|
27
|
+
## Slow-shape signals to watch for
|
|
28
|
+
|
|
29
|
+
Before creating a source, check your own SQL for these. Each is a reason to offer the user a faster shape, never to refuse:
|
|
30
|
+
|
|
31
|
+
| Signal | What it looks like | Faster option |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| Raw passthrough | No `GROUP BY` / no aggregate - one row per raw event | Pre-aggregate to the grain the dashboards chart |
|
|
34
|
+
| Very wide | Far more columns than dashboards use (e.g. `SELECT *`) | Select only the columns dashboards need |
|
|
35
|
+
| High-cardinality grain | A dimension with thousands of distinct values (ad / campaign / user ids) | Keep it out of the base; build a separate drill-down source |
|
|
36
|
+
| Large + monolithic | A big source whose typical query still scans most rows | Pre-aggregate and narrow so typical queries touch a few thousand rows |
|
|
37
|
+
|
|
38
|
+
A wide or raw source is sometimes the right call - row-level drill-down/export, columns genuinely all used, or a staging source to reshape later. Note the trade-off, then build whichever the user chooses.
|
|
39
|
+
|
|
27
40
|
## Creating data sources
|
|
28
41
|
|
|
29
42
|
`graphit ds create` auto-chains: create -> poll until ready -> scan schema -> print verification link. The user must visit the verification link on the platform to review AI-generated column descriptions and activate the DS for KB use.
|
|
@@ -50,9 +50,9 @@ Bind a chart to the range with two scalar params and a `BETWEEN`:
|
|
|
50
50
|
|
|
51
51
|
```js
|
|
52
52
|
graphit.bind('#rev', {
|
|
53
|
-
sql: 'SELECT day, SUM(rev) AS rev FROM orders WHERE day BETWEEN :
|
|
53
|
+
sql: 'SELECT day, SUM(rev) AS rev FROM orders WHERE day BETWEEN :start_date AND :end_date GROUP BY 1',
|
|
54
54
|
dataSourceId: 'ds_abc',
|
|
55
|
-
params: () => ({
|
|
55
|
+
params: () => ({ start_date: dr.start, end_date: dr.end }),
|
|
56
56
|
deps: dr.deps,
|
|
57
57
|
render: (r, el) => graphit.chart(el, { type: 'area', data: r.data, x: 'day', y: 'rev' }),
|
|
58
58
|
})
|
|
@@ -131,7 +131,9 @@ Use `:name` placeholders in SQL for safe server-side parameter binding. Never st
|
|
|
131
131
|
|-------|-----|--------|
|
|
132
132
|
| Scalar | `WHERE country = :country` | `{country: 'US'}` |
|
|
133
133
|
| Multi-select | `WHERE country IN :countries` | `{countries: ['US', 'IL']}` (expands to safe `IN ($0, $1)`) |
|
|
134
|
-
| Date range | `WHERE date BETWEEN :
|
|
134
|
+
| Date range | `WHERE date BETWEEN :start_date AND :end_date` | `{start_date: '2026-01-01', end_date: '2026-06-01'}` |
|
|
135
|
+
|
|
136
|
+
**Don't name a param after a SQL keyword** (`from`, `to`, `select`, `order`, `group`, ...). The SQL template is parsed before values bind, so a reserved-word placeholder like `:from` fails with "SQL validation failed". Use names like `:start_date`, `:end_date`.
|
|
135
137
|
|
|
136
138
|
Array length capped at 200 elements, max 50 param keys per resolve call.
|
|
137
139
|
|
|
@@ -36,8 +36,8 @@ The server compiles references to actual expressions, injects rule constraints,
|
|
|
36
36
|
| Mode | Behavior |
|
|
37
37
|
|------|----------|
|
|
38
38
|
| `observe` | All queries pass, tiers are tracked |
|
|
39
|
-
| `warn` | Ad-hoc queries on governed DSes
|
|
40
|
-
| `strict` |
|
|
39
|
+
| `warn` | Ad-hoc **measure** queries on governed DSes ask for approval; exploration warns |
|
|
40
|
+
| `strict` | All ad-hoc queries on governed DSes are blocked |
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
43
|
graphit governance status # View mode and conformance stats
|
|
@@ -45,6 +45,13 @@ graphit governance set warn # Change mode (admin only)
|
|
|
45
45
|
graphit governance audit # View audit log
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
+
## Ad-hoc Measure Gate
|
|
49
|
+
|
|
50
|
+
On a governed data source, an ad-hoc query that computes a business measure (an aggregate or `GROUP BY` producing a metric) is gated. Exploration is exempt and runs free: `SELECT *`, raw column selects, `COUNT(*)` row-count peeks, `DISTINCT` value lists.
|
|
51
|
+
|
|
52
|
+
- **warn mode:** the measure query is rejected. Rewrite using `{{metric:NAME}}` / `{{dim:NAME}}` references, or - if the user needs the raw run - ask them and re-run with `--approve-adhoc`.
|
|
53
|
+
- **strict mode:** all ad-hoc queries are hard-blocked. Rewrite with references or stop; `--approve-adhoc` does not bypass strict.
|
|
54
|
+
|
|
48
55
|
## Enforceable Rules
|
|
49
56
|
|
|
50
57
|
Rules with typed constraints are enforced automatically via SQL injection:
|
|
@@ -46,6 +46,8 @@ All KB assets are UPPER_SNAKE_CASE (auto-sanitized on write):
|
|
|
46
46
|
| `COUNT_*` | `COUNT_ACTIVE_USERS` | Count metrics |
|
|
47
47
|
| `*_RATE` | `CONVERSION_RATE`, `CHURN_RATE` | Ratios / percentages |
|
|
48
48
|
|
|
49
|
+
> `*_RATE`/ratio columns are usually 0-1 fractions. To chart them as a percent, multiply by 100 in SQL (`* 100.0`) - `"percent"` format appends `%` without scaling.
|
|
50
|
+
|
|
49
51
|
## Formula Syntax
|
|
50
52
|
|
|
51
53
|
Metrics reference `TABLE.COLUMN` with UPPERCASE naming:
|
|
@@ -127,7 +127,7 @@ Canvas `graphit.resolve()` queries that follow these shapes serve from a semanti
|
|
|
127
127
|
**Shapes that skip the cache** (fall back to normal execution, still correct):
|
|
128
128
|
- `COUNT(DISTINCT x)`, window functions, HAVING, QUALIFY
|
|
129
129
|
- OR / NOT in WHERE
|
|
130
|
-
- Ratio metrics (`SUM(a)/NULLIF(SUM(b),0)`) - compute client-side or use two resolves
|
|
130
|
+
- Ratio metrics (`SUM(a)/NULLIF(SUM(b),0)`) - compute client-side or use two resolves. To display as a percent, multiply by 100 (`* 100.0 ... AS x_pct`): the `"percent"` format only appends `%`, it does not scale (a 0-1 ratio would show as `0.42%`, not `42%`).
|
|
131
131
|
- `CURRENT_DATE`-relative predicates
|
|
132
132
|
- Top-N with aggregate only in ORDER BY (`SELECT dim FROM t GROUP BY dim ORDER BY SUM(metric) DESC LIMIT N` - no decomposable aggregate in SELECT)
|
|
133
133
|
|