@graphit/cli 0.1.19 → 0.1.22

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.
@@ -2,7 +2,7 @@ import { readFile } from "node:fs/promises";
2
2
  import { apiClient } from "../api/client.js";
3
3
  import { getFrontendUrl } from "../config.js";
4
4
  import { output, errorOutput, getOutputFormat } from "../output/format.js";
5
- const REQUIRED_ATTRS = ["data-graphit-id", "data-graphit-label", "data-graphit-kb", "data-graphit-sql", "data-graphit-ds"];
5
+ const REQUIRED_ATTRS = ["data-graphit-id", "data-graphit-label", "data-graphit-sql", "data-graphit-ds"];
6
6
  const KNOWN_ATTRS = new Set([...REQUIRED_ATTRS, "data-graphit-highlight"]);
7
7
  const TYPO_MAP = {
8
8
  "data-graphit-entity": "data-graphit-id",
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/commands/dashboard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/commands/dashboard.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphit/cli",
3
- "version": "0.1.19",
3
+ "version": "0.1.22",
4
4
  "description": "Graphit CLI - Build custom dashboards from any AI coding assistant",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,6 +22,16 @@ If the output includes an "Update available" banner, tell the user to update bef
22
22
  When any `graphit` command fails with an unexpected error (unknown command, unrecognized flag, non-zero exit with unclear message), suggest `npm update -g @graphit/cli` before investigating further. Outdated CLI versions are the most common cause of unexpected errors.
23
23
  Do NOT suggest updating for normal operational failures (expired auth, bad SQL syntax, network timeout, entity not found).
24
24
 
25
+ ## Permission Errors
26
+
27
+ The CLI enforces the same permission model as the platform. Three error codes to know:
28
+
29
+ - **403 "This feature requires an Analyst seat"** - viewer-seat users are blocked from all CLI commands except `graphit auth` and `graphit me`. The CLI is an analyst tool; viewers use the platform UI.
30
+ - **404 "not found"** - returned for dashboards the user cannot access (private dashboards owned by others, team dashboards the user is not on). The API intentionally does not distinguish "does not exist" from "you cannot access it" to prevent ID enumeration.
31
+ - **423 "Shared dashboard requires an active editing session"** - shared dashboard mutations require the user to enter Edit mode on the platform first (see constraint #4 below).
32
+
33
+ Connector create/delete are restricted to org admins (owner/admin role). Non-admin analysts get 403 on these commands.
34
+
25
35
  ## After Setup
26
36
 
27
37
  After `graphit setup` completes successfully, offer to add a Graphit section to the project's CLAUDE.md (or AGENTS.md for Codex) so future sessions know Graphit is available without activating the skill. Suggested snippet:
@@ -59,13 +69,12 @@ NEVER query the warehouse directly when a cached data source covers the table. D
59
69
  **Use the data source name in SQL** (e.g. `FROM MARKETING_UA_DS`), not the raw source table. The DS is a KB table - governance rules target it directly, and users recognize DS names from the platform.
60
70
 
61
71
  ### 3. EVERY element must have entity wrapping
62
- Without `data-graphit-*` attributes, elements are invisible to the platform - no click info, no mentions, no KB provenance. Every chart, KPI card, table, and text section needs ALL FIVE attributes:
72
+ Without `data-graphit-*` attributes, elements are invisible to the platform - no click info, no mentions, no KB provenance. Every chart, KPI card, table, and text section needs ALL FOUR attributes:
63
73
 
64
74
  ```html
65
75
  <div data-graphit-id="revenue-trend"
66
76
  data-graphit-label="Revenue Trend"
67
- data-graphit-kb="metric:REVENUE,dimension:REGION,table:ORDERS"
68
- data-graphit-sql="SELECT region, SUM(revenue) FROM orders GROUP BY region"
77
+ data-graphit-sql="SELECT {{dim:REGION}} AS region, {{metric:REVENUE}} AS revenue FROM orders GROUP BY region"
69
78
  data-graphit-ds="ds_abc123">
70
79
  <!-- chart/KPI/table content here -->
71
80
  </div>
@@ -75,11 +84,10 @@ Without `data-graphit-*` attributes, elements are invisible to the platform - no
75
84
  |-----------|--------|---------|
76
85
  | `data-graphit-id` | Unique kebab-case | `"spend-by-source"` |
77
86
  | `data-graphit-label` | Human-readable name | `"Ad Spend by Source"` |
78
- | `data-graphit-kb` | `type:NAME` comma-separated | `"metric:CPI,dimension:MEDIA_SOURCE,table:MARKETING_UA_DS"` |
79
87
  | `data-graphit-sql` | Executable SQL (HTML-encode `<>&"`) | `"SELECT ..."` |
80
88
  | `data-graphit-ds` | Data source ID from `graphit ds list` | `"ds_abc123"` |
81
89
 
82
- KB types: `metric`, `dimension`, `table`, `rule`. Names are UPPER_SNAKE_CASE matching the KB exactly. **Names are unique per type per org** - creating a duplicate name will fail. Check `graphit kb list <type>` first; update the existing asset instead of creating a new one.
90
+ KB asset references are derived automatically from `{{metric:X}}` / `{{dim:X}}` templates in your SQL. The platform's governance compiler resolves these and displays KB asset chips in the entity details panel.
83
91
  Missing any attribute = broken entity. Missing wrapping entirely = invisible to the platform.
84
92
 
85
93
  **SQL must be complete and executable.** The platform runs `data-graphit-sql` against the data source when a user opens the entity's details panel. Write the full query from the `graphit.resolve()` call - never abbreviate, truncate, or use placeholders (`FROM ...`, `SELECT ...`, ellipsis). It MUST use the real DS table name and only columns that exist in the DS - never invented summary columns, CTE aliases, JS variable names, or prose. If the chart's resolve call uses a CTE, put the full WITH query. If JS builds SQL dynamically, store one representative executable variant (e.g. the default date range).
@@ -108,19 +116,32 @@ NEVER embed query results as static JS variables. The dashboard iframe provides
108
116
 
109
117
  ---
110
118
 
119
+ ## How to Work (governs both workflows below)
120
+
121
+ You are a colleague building WITH the user, not a batch job that explores in silence and returns a finished product. Two habits make the difference: **short iterations** and **narration**.
122
+
123
+ **Short iterations.** Do one thing, show it, let the user react, then do the next: explore the KB and report what you found; validate one query and show the rows; build one section and show it. Each small step is a cheap chance for the user to redirect before you have built in the wrong direction. The opposite - exploring silently, deciding by yourself whether the data will work, then dropping a finished 600-line dashboard (or a bulk KB write) on the user - forces them to either accept a wrong result or send you back to the start.
124
+
125
+ **Always narrate.** The user cannot see your command output: the KB you listed, the SQL you ran, the rows that came back are invisible unless you surface them. So after each step, say what you found, what it means, and what you are about to do next. The one exception: if the user says "just build it" or "go", drop the running commentary and work straight through. Match the user's mode.
126
+
127
+ The difference in practice:
128
+ - **Weak (solo):** silently list the KB, silently run several queries, then save a complete dashboard and announce "Done - here's your dashboard."
129
+ - **Strong (collaborative):** "Found a **Marketing UA** data source with `{{metric:CPI}}` and `{{metric:ROAS}}` already defined. Starting with a spend-vs-installs trend - querying now." Then, after showing the rows: "Spend tracks installs except in March. Want that as the first graph, or should I look at ROAS first?"
130
+
131
+ The numbered steps in each workflow run *inside* this loop, not instead of it.
132
+
111
133
  ## Workflow: Dashboard Build
112
134
 
113
- 1. **Ask the user** what dashboard they want. Don't start querying until you know what they need.
114
- 2. **Explore KB** - use `graphit kb list domains` then `graphit kb explore domain <NAME>` to traverse existing assets. Search by concept with `graphit kb search "revenue"`. See `kb-exploration.md` for KB-first discovery, `kb-traversal.md` for tool selection.
115
- 3. **Find a data source** (`graphit ds list`) - prefer cached data sources (~100ms) over live warehouse (~10s).
116
- 4. **Query data to validate** - run queries via the CLI to verify SQL and preview results. Show the user what you found.
117
- 5. **Build HTML** - write `graphit.resolve()` calls for live data + `graphit.chart/table/kpi` for rendering. All CSS in `<style>`, all JS in `<script>`. Write to a local `.html` file.
118
- 6. **Save** with `graphit dashboard update-html <id> --file <path>`. If the response contains `entity_sql_warnings`, an entity's `data-graphit-sql` is missing, matches no data source, or fails against the DS schema - fix the flagged entities and save again before finishing.
119
- 7. Give the user the dashboard URL so they can open it.
135
+ 1. **Understand.** Ask what the dashboard should answer. Don't query until you know the goal - one clarifying question beats a wrong dashboard.
136
+ 2. **Scope to the domain, then work down: domain -> data source -> assets.** Start narrow, not broad. Ask the user which business domain this dashboard is about (or infer it and confirm) - that scopes everything that follows. Then `graphit kb explore domain <NAME>` to see that domain's data sources and the metrics and dimensions defined on them. Reach for broad `graphit kb search` only as a fallback - when the domain is unclear or a concept spans domains. See `kb-discovery.md` and `kb-traversal.md`. Then tell the user what you found - the data source, the metrics and dimensions you'll use (by name), and the graphs you plan - before building.
137
+ 3. **Pick the data source, and handle gaps.** Use the cached data source in that domain (~100ms, preferred over live warehouse at ~10s); use its name in SQL (`FROM MARKETING_UA_DS`). If that data source is missing something you need, first look for a connection to another data source you can join (`graphit kb list relationships`); only if the data genuinely does not exist yet, propose building a new data source rather than forcing a bad query.
138
+ 4. **Validate each query, and show the result.** Write SQL with `{{metric:NAME}}` / `{{dim:NAME}}` reference syntax for KB assets, run it with `--verbose`, and for EVERY query show the user a compact result: the reference-syntax query, a small markdown table of rows, the row count, and the trust tier. If a query returns zero rows or nulls, say so and diagnose (wrong table? filter too narrow?).
139
+ 5. **Get approval, then build.** Once the user is happy with the data and plan, assemble the HTML: `graphit.resolve()` for live data, `graphit.chart/table/kpi` for rendering, every entity wrapped (see HARD CONSTRAINTS), all CSS in `<style>`, all JS in `<script>`. For a large dashboard, add entities incrementally - build one, show it, add the next - rather than generating everything at once. Write to a local `.html` file.
140
+ 6. **Save - prefer entity updates - then hand off.** New dashboard: `graphit dashboard update-html <id> --file <path>`. Changing an existing one: prefer `graphit dashboard update-entity <id> <entity_id>`, which updates a single entity without regenerating (and destroying) the rest. If the response contains `entity_sql_warnings`, an entity's `data-graphit-sql` is missing, matches no data source, or fails the DS schema - fix the flagged entities and save again before reporting done. Confirm the save succeeded, then give the user the dashboard URL.
120
141
 
121
142
  ## Workflow: KB Build / Onboarding
122
143
 
123
- When the user wants to build or populate their KB (from files, schema descriptions, or scratch). Consult `kb-graph-structure.md` for the full graph model, `kb-traversal.md` for tool selection, `kb-actions.md` for the CLI command matrix, `kb-awareness.md` for when to propose asset creation, and `parameterized-metrics.md` for template metrics.
144
+ When the user wants to build or populate their KB (from files, schema descriptions, or scratch). Consult `kb-structure.md` for the full graph model and structural Q&A, `kb-traversal.md` for tool selection, `kb-actions.md` for the CLI command matrix, `kb-discovery.md` for when to propose asset creation, and `parameterized-metrics.md` for template metrics.
124
145
 
125
146
  **Step 1: Audit current KB state.** Before proposing anything, traverse the existing KB:
126
147
 
@@ -128,9 +149,9 @@ When the user wants to build or populate their KB (from files, schema descriptio
128
149
  2. For each domain: `graphit kb explore domain <NAME>` - returns all tables and assets in that domain in one traversal (see `kb-traversal.md` depth guidelines)
129
150
  3. `graphit kb list synonyms` and `graphit kb list relationships` - global assets not scoped to domains
130
151
 
131
- Present a tree summary of what already exists, following the Domain > Table > Topic > Asset hierarchy from `kb-graph-structure.md`.
152
+ Present a tree summary of what already exists, following the Domain > Table > Topic > Asset hierarchy from `kb-structure.md`.
132
153
 
133
- **Step 2: Analyze input.** Read the user's files, schema, or description. Classify every concept by asset type. Use `kb-explanation.md` for asset type definitions (what is a metric vs dimension vs rule). Use `kb-awareness.md` for signals (aggregation = metric, derived grouping = dimension, business constraint = rule, colloquial term = synonym).
154
+ **Step 2: Analyze input.** Read the user's files, schema, or description. Classify every concept by asset type. Use `kb-structure.md` for asset type definitions (what is a metric vs dimension vs rule). Use `kb-discovery.md` for signals (aggregation = metric, derived grouping = dimension, business constraint = rule, colloquial term = synonym).
134
155
 
135
156
  **Step 3: Present a tree-structured plan.** Show what will be created, organized by the KB hierarchy. Mark existing assets with (exists) so the user sees what's new vs what's already there:
136
157
 
@@ -288,6 +309,8 @@ The `dataSourceId` is the ID from `graphit ds list`. The `target` parameter (CSS
288
309
 
289
310
  **Error handling:** `graphit.resolve()` rejects on timeout (120s), bad SQL, or invalid data source ID. Wrap calls in try/catch and show a user-visible error message in the target element on failure. Verify SQL returns data via the CLI before embedding it in HTML.
290
311
 
312
+ **Rate limit:** 120 requests per minute per user per dashboard. Each `graphit.resolve()` call counts as one request. A dashboard with 6 KPIs/charts that refreshes on every filter change uses 6 requests per interaction - budget for ~20 filter changes per minute. If you hit the limit, the API returns a "Too many requests" error with a retry-after hint. Design dashboards to batch all queries in a single `Promise.all` so they count against the same time window rather than spreading across multiple refresh calls.
313
+
291
314
  ### First-paint loading state
292
315
 
293
316
  The dashboard HTML paints before the SDK connects (iframe load + handshake), so the SDK's own spinner cannot cover the first moments. Bake this pure-CSS overlay into the HTML so every chart shows a spinner from the first frame; the SDK adopts it and removes it when that element's `graphit.resolve()` settles (success or error).
@@ -335,8 +358,7 @@ These are shortcuts, not requirements. Use them when a standard chart is all you
335
358
  ```html
336
359
  <div data-graphit-id="spend-by-source"
337
360
  data-graphit-label="Ad Spend by Source"
338
- data-graphit-kb="metric:CPI,dimension:MEDIA_SOURCE,table:MARKETING_UA_DS"
339
- data-graphit-sql="SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) as spend FROM MARKETING_UA_DS GROUP BY MEDIA_SOURCE ORDER BY spend DESC"
361
+ data-graphit-sql="SELECT {{dim:MEDIA_SOURCE_DIMENSION}} AS source, {{metric:CPI}} AS cpi FROM MARKETING_UA_DS GROUP BY source ORDER BY cpi DESC"
340
362
  data-graphit-ds="ds_abc123">
341
363
  <div id="spend-chart" class="gh-loading">
342
364
  <div class="gh-loading-overlay"><svg class="gh-loading-spin" width="24" height="24" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="var(--graphit-border,#e5e5e5)" stroke-width="2.5"/><path d="M12 2a10 10 0 0 1 10 10" stroke="var(--graphit-accent,#4DB6AC)" stroke-width="2.5" stroke-linecap="round"/></svg></div>
@@ -367,10 +389,8 @@ Detailed knowledge lives in `references/`. Consult the relevant file when you ne
367
389
  |------|-------------|
368
390
  | `dashboard-planning.md` | Building a multi-chart dashboard. Covers framing the question, picking archetype, mandatory rules, metric contracts, anti-patterns. |
369
391
  | `chart-selection.md` | Choosing chart types. Full dimension/measure defaults, perception ranking, cardinality guards, hard caps. |
370
- | `kb-exploration.md` | Starting a build. KB-first discovery, metric vs dimension, naming conventions, formula syntax. |
371
- | `kb-awareness.md` | KB-first planning. When to propose KB asset creation, reuse over reinvention, cross-table referencing, topic assignment. |
372
- | `kb-graph-structure.md` | KB graph model. 9 node types, 5 edge types, multi-membership semantics, domain cascade, tree rendering order. |
373
- | `kb-explanation.md` | Structural Q&A. What are domains, topics, tables, relationships, memory. Why assets appear where they do. |
392
+ | `kb-discovery.md` | Starting a build (domain-first). Domain -> DS -> assets discovery, metric vs dimension, naming, formula syntax, when to propose asset creation, reuse over reinvention, cross-table referencing. |
393
+ | `kb-structure.md` | KB graph model + structural Q&A. Node types, edge types, the vertical home (domain -> DS -> assets) vs horizontal topics, tree order, and how to answer "what is a domain" / "why is X under Y". |
374
394
  | `kb-traversal.md` | Graph queries. Tool selection guide (search vs explore vs read), common query patterns, depth guidelines. |
375
395
  | `kb-actions.md` | Full CRUD matrix. Asset create/edit/delete, topic management, domain assignment cascade, navigation and discovery. |
376
396
  | `parameterized-metrics.md` | Metric templates. ${PARAM:NAME} tokens, child variant auto-generation, editing templates, using variants in queries. |
@@ -380,3 +400,4 @@ Detailed knowledge lives in `references/`. Consult the relevant file when you ne
380
400
  | `chart-patterns.md` | Custom chart implementations. Inline SVG/CSS code for: scatter/bubble, heatmap, funnel, gauge, sparkline, stacked bar, and the shared tooltip pattern. |
381
401
  | `governance.md` | Query governance. Reference syntax, trust tiers, enforceable rules, override flow, governance commands. |
382
402
  | `presentations.md` | Slide deck presentations. Builder API, layouts (center/split/full), backgrounds, navigation, live data inside slides. |
403
+ | `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. |
@@ -0,0 +1,31 @@
1
+ ---
2
+ alwaysApply: false
3
+ description: "Graphit: Consult when creating or shaping a data source. A source's shape (set by its source SQL) decides dashboard speed and whether filter changes are cacheable: pre-aggregate to the query grain, narrow columns, keep high-cardinality dimensions out of the base."
4
+ globs: []
5
+ ---
6
+
7
+ # Data Sources: Building for Speed
8
+
9
+ A data source's shape - set by its source SQL at creation - decides how fast every dashboard built on it will be, and whether filter changes feel instant. Get this right when you create the source; it can't be fixed later in the dashboard SQL.
10
+
11
+ ## Why source shape decides dashboard speed
12
+
13
+ When you change a dashboard filter (e.g. switch country), Graphit answers from a cached, pre-aggregated result instead of re-scanning the whole source - but only when the source is small and already aggregated to the grain you query. Build the source as one row per the grain you'll chart (e.g. month x country x channel) with only the columns you use, and filter changes return in milliseconds. Pull in raw ungrouped rows, hundreds of columns, or thousands-of-values dimensions and the result is too big to cache - every filter change re-scans the entire source and is slow.
14
+
15
+ ## Build it right (in the source SQL)
16
+
17
+ 1. **Pre-aggregate to your query grain.** Use `GROUP BY` so the source has one row per the grain you'll chart (month x country x channel), not one row per raw event. The single biggest speed lever.
18
+ 2. **Select only the columns you'll use.** Every column is downloaded and cached on each query; hundreds of columns slow every dashboard, even ones that touch a few.
19
+ 3. **Keep high-cardinality dimensions out of the base.** Individual ad / campaign / adset names (thousands of values) explode aggregation cost and block filter-caching. If you need them, build a separate filtered drill-down source.
20
+ 4. **Aim small.** A source whose typical query aggregates a few thousand rows is fast and cacheable; a 50M-row, 400-column raw source is slow no matter how the dashboard is written. The size guardrails (>100M rows / >5GB) are the ceiling, not the target.
21
+
22
+ ## Quick check
23
+
24
+ | Lever | Build it right | Anti-pattern |
25
+ |---|---|---|
26
+ | Grain | `GROUP BY` to the grain you chart | One row per raw event |
27
+ | Columns | Only the columns dashboards use | 400+ columns "just in case" |
28
+ | Cardinality | Low-card dimensions in the base; ad/campaign names in a separate drill-down | Thousands-of-values dimensions in the base grain |
29
+ | Size | A few-thousand-row typical aggregation | Sitting at the 100M-row / 5GB ceiling |
30
+
31
+ Build the source small and pre-aggregated, and dashboards on it stay fast and filterable.
@@ -28,13 +28,12 @@ Run `graphit ds list` FIRST. If a DS covers your table, use `graphit query "SQL"
28
28
  **Use the DS name in SQL, not the source table name.** Write `FROM MARKETING_UA_DS`, not `FROM MARKETING_UA`. The DS name is what users see in the platform. DuckDB resolves both, but the DS name keeps SQL consistent with the UI.
29
29
 
30
30
  ### 3. EVERY element must have entity wrapping
31
- Without `data-graphit-*` attributes, elements are invisible to the platform. Every chart, KPI card, table, and text section needs ALL FIVE attributes:
31
+ Without `data-graphit-*` attributes, elements are invisible to the platform. Every chart, KPI card, table, and text section needs ALL FOUR attributes:
32
32
 
33
33
  ```html
34
34
  <div data-graphit-id="revenue-trend"
35
35
  data-graphit-label="Revenue Trend"
36
- data-graphit-kb="metric:REVENUE,dimension:REGION,table:ORDERS"
37
- data-graphit-sql="SELECT region, SUM(revenue) FROM orders GROUP BY region"
36
+ data-graphit-sql="SELECT {{dim:REGION}} AS region, {{metric:REVENUE}} AS revenue FROM orders GROUP BY region"
38
37
  data-graphit-ds="ds_abc123">
39
38
  <!-- chart/KPI/table content here -->
40
39
  </div>
@@ -44,18 +43,21 @@ Without `data-graphit-*` attributes, elements are invisible to the platform. Eve
44
43
  |-----------|--------|---------|
45
44
  | `data-graphit-id` | Unique kebab-case | `"spend-by-source"` |
46
45
  | `data-graphit-label` | Human-readable name | `"Ad Spend by Source"` |
47
- | `data-graphit-kb` | `type:NAME` comma-separated | `"metric:CPI,dimension:MEDIA_SOURCE,table:MARKETING_UA_DS"` |
48
46
  | `data-graphit-sql` | Executable SQL (HTML-encode `<>&"`) | `"SELECT ..."` |
49
47
  | `data-graphit-ds` | Data source ID from `graphit ds list` | `"ds_abc123"` |
50
48
 
51
- KB types: `metric`, `dimension`, `table`, `rule`. Names are UPPER_SNAKE_CASE matching the KB exactly. **Names are unique per type per org** - creating a duplicate name will fail. Check `graphit kb list <type>` first; update the existing asset instead of creating a new one.
49
+ KB asset references are derived automatically from `{{metric:X}}` / `{{dim:X}}` templates in your SQL.
52
50
 
53
51
  **SQL must be complete and executable.** The platform runs `data-graphit-sql` against the data source when a user opens the entity's details panel. Write the full query from the `graphit.resolve()` call - never abbreviate, truncate, or use placeholders (`FROM ...`, `SELECT ...`, ellipsis). It MUST use the real DS table name and only columns that exist in the DS - never invented summary columns, CTE aliases, JS variable names, or prose. If the chart's resolve call uses a CTE, put the full WITH query. If JS builds SQL dynamically, store one representative executable variant (e.g. the default date range).
54
52
 
55
53
  **Label = visible title.** The `data-graphit-label` MUST match the card's visible heading exactly.
56
54
 
57
- ### 4. Shared dashboards require Edit mode on the platform
58
- Dashboards shared with teams are protected by an editing session lock. The CLI cannot acquire editing sessions - mutations (`dashboard update-html`, `dashboard update-entity`) on a shared dashboard will fail with 423. Tell the user to open the dashboard on the Graphit platform and click **Edit** first. Private dashboards are unaffected.
55
+ ### 4. Permission errors
56
+ The CLI enforces the same permission model as the platform:
57
+ - **403 "Analyst seat required"** - viewer-seat users are blocked from all CLI commands except `graphit auth` and `graphit me`. Viewers use the platform UI.
58
+ - **404 "not found"** - returned for dashboards the user cannot access (private dashboards owned by others, team dashboards the user is not on). The API does not distinguish "does not exist" from "you cannot access it."
59
+ - **423 "Shared dashboard requires an active editing session"** - shared dashboard mutations require the user to enter Edit mode on the platform first. Tell the user to open the dashboard and click **Edit**. Private dashboards are unaffected.
60
+ - Connector create/delete are restricted to org admins (owner/admin role).
59
61
 
60
62
  ### 5. ALWAYS use graphit.resolve() for live data
61
63
  NEVER embed query results as static JS variables. The dashboard iframe provides `graphit.resolve()` which fetches live data from cached data sources on every page load.
@@ -109,7 +111,7 @@ You are the rendering layer - format and present every CLI result using markdown
109
111
 
110
112
  ## Workflow: KB Build / Onboarding
111
113
 
112
- When the user wants to build or populate their KB from files, schemas, or scratch. Consult companion rules: graphit-kb-graph-structure (graph model), graphit-kb-traversal (tool selection), graphit-kb-actions (CLI commands), graphit-kb-awareness (when to propose assets), graphit-parameterized-metrics (template metrics).
114
+ When the user wants to build or populate their KB from files, schemas, or scratch. Consult companion rules: graphit-kb-graph-structure (graph model), graphit-kb-traversal (tool selection), graphit-kb-actions (CLI commands), graphit-kb-awareness (when to propose assets), graphit-parameterized-metrics (template metrics), graphit-data-sources (shaping sources for speed: pre-aggregate to grain, narrow columns, keep high-cardinality dimensions out of the base).
113
115
 
114
116
  1. **Audit current KB state FIRST.** Traverse, don't just list: `graphit kb list domains`, then `graphit kb explore domain <NAME>` for each (returns full tree in one call). Add `graphit kb list synonyms` and `graphit kb list relationships` for globals. Present tree summary.
115
117
  2. **Analyze input.** Read the user's files/schema. Classify every concept by asset type (see graphit-kb-explanation for definitions, graphit-kb-awareness for signals).
@@ -153,6 +155,8 @@ const result = await graphit.resolve({
153
155
 
154
156
  **Error handling:** Rejects on timeout (120s), bad SQL, or invalid data source ID. Wrap in try/catch.
155
157
 
158
+ **Rate limit:** 120 requests/min per user per dashboard. A 6-chart dashboard uses 6 requests per filter change (~20 changes/min budget). Batch all queries in a single `Promise.all`.
159
+
156
160
  ### First-paint loading state
157
161
 
158
162
  The HTML paints before the SDK connects, so bake a pure-CSS spinner overlay into the page; the SDK adopts it and removes it when that element's `graphit.resolve()` settles (success or error). Add once to the page `<style>`:
@@ -181,8 +185,7 @@ Add the overlay inside EVERY element you pass as `target:` - and ONLY those elem
181
185
  ```html
182
186
  <div data-graphit-id="spend-by-source"
183
187
  data-graphit-label="Ad Spend by Source"
184
- data-graphit-kb="metric:CPI,dimension:MEDIA_SOURCE,table:MARKETING_UA_DS"
185
- data-graphit-sql="SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) as spend FROM MARKETING_UA_DS GROUP BY MEDIA_SOURCE ORDER BY spend DESC"
188
+ data-graphit-sql="SELECT {{dim:MEDIA_SOURCE_DIMENSION}} AS source, {{metric:CPI}} AS cpi FROM MARKETING_UA_DS GROUP BY source ORDER BY cpi DESC"
186
189
  data-graphit-ds="ds_abc123">
187
190
  <div id="spend-chart" class="gh-loading">
188
191
  <div class="gh-loading-overlay"><svg class="gh-loading-spin" width="24" height="24" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="var(--graphit-border,#e5e5e5)" stroke-width="2.5"/><path d="M12 2a10 10 0 0 1 10 10" stroke="var(--graphit-accent,#4DB6AC)" stroke-width="2.5" stroke-linecap="round"/></svg></div>
@@ -1,6 +1,6 @@
1
1
  # Chart Patterns
2
2
 
3
- All chart types are available via `graphit.chart()`. All data comes from `graphit.resolve()` - never embed static data.
3
+ The chart types documented below are the native `graphit.chart()` types. Anything not listed here (treemap, sankey, maps, box) is hand-rolled SVG - see `chart-selection.md` for the native-vs-hand-rolled split. All data comes from `graphit.resolve()` - never embed static data.
4
4
 
5
5
  **NEVER use `<canvas>`.** Canvas produces blurry charts inside the sandboxed iframe due to DPI scaling issues.
6
6
 
@@ -54,18 +54,7 @@ Templates are org-specific - they exist only when users have saved them. The age
54
54
 
55
55
  ## Color tokens
56
56
 
57
- | Token | Usage |
58
- |---|---|
59
- | `var(--graphit-accent)` | Primary brand teal |
60
- | `var(--graphit-success)` | Positive/good |
61
- | `var(--graphit-warning)` | Caution |
62
- | `var(--graphit-error)` | Negative/bad |
63
- | `var(--graphit-fg)` | Primary text |
64
- | `var(--graphit-fg-muted)` | Secondary text |
65
- | `var(--graphit-fg-subtle)` | Labels, placeholders |
66
- | `var(--graphit-border)` | Borders, grid lines |
67
- | `var(--graphit-surface-raised)` | Card backgrounds |
68
- | `var(--graphit-surface-sunken)` | Inset areas |
57
+ Use theme CSS variables for all structural colors so charts adapt to light and dark mode. The full token table and usage rules live in `graphit-style.md` - that is the single source; do not re-list tokens here. Chart series colors come from the runtime palette automatically.
69
58
 
70
59
  ## Tooltip pattern (for hand-rolled charts)
71
60
 
@@ -17,23 +17,26 @@ When ambiguous, propose 2-3 options and ask the user. Do not guess.
17
17
 
18
18
  ## Full Chart Type Table
19
19
 
20
- | Data shape | Chart type | Required columns |
21
- |---|---|---|
22
- | Time series | line | 1 temporal + 1 numeric |
23
- | Categories | bar | 1 categorical + 1 numeric |
24
- | Stacked areas | area | 1 temporal/categorical + 1 numeric |
25
- | Part-whole (max 5 slices) | pie | 1 categorical + 1 numeric |
26
- | Single metric | KPI card | 1 numeric |
27
- | Stages | funnel | 1 categorical + 1 numeric |
28
- | Target/progress | gauge | 1 numeric |
29
- | Matrix (unpivoted) | heatmap | 2 categorical + 1 numeric |
30
- | Hierarchy | treemap | 1 categorical + 1 numeric |
31
- | Flows | sankey | 2 categorical + 1 numeric |
32
- | Correlation | scatter | 2 numeric |
33
- | 3 variables | bubble | 3 numeric |
34
- | Countries | choropleth map | 1 categorical + 1 numeric |
35
- | Lat/lng | scatter map | 2 numeric (lat + lng) |
36
- | Detail/raw data | table | any columns |
20
+ `graphit.chart()` renders only the **native** types below and throws `Unsupported graphit.chart type` on anything else. The iframe still lets you draw anything with inline SVG/CSS, so the **hand-rolled** shapes are fully buildable - just not through `graphit.chart()` (see `chart-patterns.md`). Never pass a hand-rolled type name to `graphit.chart()`.
21
+
22
+ | Data shape | Chart type | Render with | Columns |
23
+ |---|---|---|---|
24
+ | Time series | line / area | `graphit.chart` (native) | 1 temporal + 1 numeric |
25
+ | Categories | bar | `graphit.chart` (native) | 1 categorical + 1 numeric |
26
+ | Long category labels | horizontal-bar | `graphit.chart` (native) | 1 categorical + 1 numeric |
27
+ | Part-whole (max 5 slices) | donut / pie | `graphit.chart` (native) | 1 categorical + 1 numeric |
28
+ | Single metric | KPI card | `graphit.kpi` (native) | 1 numeric |
29
+ | Stages | funnel | `graphit.chart` (native) | 1 categorical + 1 numeric |
30
+ | Target / progress | gauge | `graphit.chart` (native) | 1 numeric |
31
+ | Matrix (unpivoted) | heatmap | `graphit.chart` (native) | 2 categorical + 1 numeric |
32
+ | Correlation | scatter | `graphit.chart` (native) | 2 numeric |
33
+ | 3 variables | bubble | `graphit.chart` (native) | 3 numeric |
34
+ | Inline trend | sparkline | `graphit.chart` (native) | 1 numeric series |
35
+ | Detail / raw data | table | `graphit.table` (native) | any columns |
36
+ | Hierarchy | treemap | hand-rolled SVG | 1 categorical + 1 numeric |
37
+ | Flows | sankey | hand-rolled SVG | 2 categorical + 1 numeric |
38
+ | Geographic | map | hand-rolled SVG | region or lat/lng + 1 numeric |
39
+ | Distribution | histogram / box | hand-rolled SVG | 1 numeric |
37
40
 
38
41
  ## Perception Ranking (Cleveland-McGill)
39
42
 
@@ -53,6 +56,8 @@ Position > length > angle > area > color.
53
56
  | KPI vs target | big-number + sparkline | gauge (unbounded) |
54
57
  | 2D matrix / cohort | heatmap | bar with 100+ |
55
58
 
59
+ The native `graphit.chart` first choices here are line, bar, stacked-bar, scatter, bubble, funnel, heatmap, gauge, and sparkline. Treemap, histogram, and box are hand-rolled SVG (see the table above) - draw them, don't pass them to `graphit.chart()`.
60
+
56
61
  ## Cardinality Guards
57
62
 
58
63
  | Cardinality of categorical dim | Action |
@@ -82,6 +82,16 @@ Purpose before data. The first response should mirror the user's intent and ask
82
82
  - User says "active users" - ask: what defines active? (logged in? performed action? within what window?)
83
83
  - User mentions MQL/SQL - ask: how does your org define the handoff? (Same column can mean 13% or 40%)
84
84
 
85
+ ## Performance
86
+
87
+ `graphit.resolve()` is rate-limited to 120 requests/min per user per dashboard. Each call counts as one request. Design for efficiency:
88
+
89
+ - **Single refresh function.** All queries in ONE `Promise.all` inside one `refresh()` function. Never scatter `graphit.resolve()` across independent event handlers or timeouts - that turns one user action into multiple bursts.
90
+ - **Count your queries per interaction.** 6 charts = 6 requests per filter change = 20 changes/min budget. 12 charts = 10 changes/min. If you have 10+ charts with 3+ filters, consider debouncing filter changes (300ms) so rapid clicks don't each trigger a full refresh.
91
+ - **Reuse trend data for KPIs.** If you already fetch a weekly time series (`SELECT week, SUM(spend) ...`), derive the KPI total and sparkline from that result set in JS instead of running a separate aggregate query. One query serves both the chart and the KPI card.
92
+ - **Avoid redundant refreshes.** If a filter only affects some charts, split into targeted refresh functions (`refreshKPIs()`, `refreshCharts()`) so unchanged sections don't re-query.
93
+ - **No polling.** Never `setInterval(refresh, ...)`. Data sources update on their own schedule - a dashboard that polls wastes the entire rate budget.
94
+
85
95
  ## Pre-Build Checklist
86
96
 
87
97
  Before generating the HTML:
@@ -0,0 +1,25 @@
1
+ # Data Sources: Building for Speed
2
+
3
+ A data source's shape - set by its source SQL at creation - decides how fast every dashboard built on it will be, and whether filter changes feel instant. Get this right when you create the source; it can't be fixed later in the dashboard SQL.
4
+
5
+ ## Why source shape decides dashboard speed
6
+
7
+ When you change a dashboard filter (e.g. switch country), Graphit answers from a cached, pre-aggregated result instead of re-scanning the whole source - but only when the source is small and already aggregated to the grain you query. Build the source as one row per the grain you'll chart (e.g. month x country x channel) with only the columns you use, and filter changes return in milliseconds. Pull in raw ungrouped rows, hundreds of columns, or thousands-of-values dimensions and the result is too big to cache - every filter change re-scans the entire source and is slow.
8
+
9
+ ## Build it right (in the source SQL)
10
+
11
+ 1. **Pre-aggregate to your query grain.** Use `GROUP BY` so the source has one row per the grain you'll chart (month x country x channel), not one row per raw event. The single biggest speed lever.
12
+ 2. **Select only the columns you'll use.** Every column is downloaded and cached on each query; hundreds of columns slow every dashboard, even ones that touch a few.
13
+ 3. **Keep high-cardinality dimensions out of the base.** Individual ad / campaign / adset names (thousands of values) explode aggregation cost and block filter-caching. If you need them, build a separate filtered drill-down source.
14
+ 4. **Aim small.** A source whose typical query aggregates a few thousand rows is fast and cacheable; a 50M-row, 400-column raw source is slow no matter how the dashboard is written. The size guardrails (>100M rows / >5GB) are the ceiling, not the target.
15
+
16
+ ## Quick check
17
+
18
+ | Lever | Build it right | Anti-pattern |
19
+ |---|---|---|
20
+ | Grain | `GROUP BY` to the grain you chart | One row per raw event |
21
+ | Columns | Only the columns dashboards use | 400+ columns "just in case" |
22
+ | Cardinality | Low-card dimensions in the base; ad/campaign names in a separate drill-down | Thousands-of-values dimensions in the base grain |
23
+ | Size | A few-thousand-row typical aggregation | Sitting at the 100M-row / 5GB ceiling |
24
+
25
+ Build the source small and pre-aggregated, and dashboards on it stay fast and filterable.
@@ -4,6 +4,8 @@ Consult when the user's data signals match a specific business domain. Each lens
4
4
 
5
5
  Detect the domain from column names and user intent. Apply universal planning rules first (see dashboard-planning.md), then layer domain-specific patterns.
6
6
 
7
+ Several high-value lens charts - **waterfall** (P&L / ARR bridges), **bullet** (KPI vs target), **Lorenz** (revenue concentration), and **histogram** (attainment distribution) - are NOT native `graphit.chart()` types, so `graphit.chart()` throws on those names. Build them as hand-rolled SVG (see `chart-selection.md` for the native-vs-hand-rolled split). The native lens charts (line, bar, stacked-bar, heatmap, funnel, KPI, sparkline) render through `graphit.chart` / `graphit.kpi`.
8
+
7
9
  ---
8
10
 
9
11
  ## Marketing & Attribution
@@ -1,99 +1,46 @@
1
- # KB Actions - CLI Parity Matrix
1
+ # KB Actions (CLI)
2
2
 
3
- Every KB action has a CLI equivalent. The tool syntax below maps to CLI commands:
3
+ Every KB asset has full create / update / delete through the `graphit kb` commands - this is the authoring reference. SKILL.md's command table is the quick glance; this file adds the create flags and the task recipes that take more than one command. All names are UPPER_SNAKE_CASE.
4
4
 
5
- | Agent Tool Syntax | CLI Command |
5
+ ## Create
6
+
7
+ | Asset | Command |
6
8
  |---|---|
7
- | `create(entity_type="metric", name=..., calculation=...)` | `graphit kb create metric --name X --sql "..." --table T` (optional `--default-dimensions "D1,D2"`) |
8
- | `create(entity_type="dimension", name=..., expression=...)` | `graphit kb create dimension --name X --expr "..." --table T` (type auto-inferred; override with `--type` / `--output-type`) |
9
- | `create(entity_type="rule", name=..., rule_text=...)` | `graphit kb create rule --name X --sql "..." --table T` |
10
- | `create(entity_type="synonym", term=..., canonical=...)` | `graphit kb create synonym --term X --canonical Y --type metric` |
11
- | `create(entity_type="domain", name=...)` | `graphit kb create domain --name X --description "..."` |
12
- | `create(entity_type="relationship", ...)` | `graphit kb create relationship --name X --primary-table T --primary-column C --related-table T2 --related-column C2` |
13
- | `create(entity_type="topic_metadata", name=...)` | `graphit kb create topic --name X --description "..."` |
14
- | `edit(entity_type=..., id=..., field=value)` | `graphit kb update <type> NAME --field value` |
15
- | `edit(entity_type="table", id=..., domain_id=...)` | `graphit kb update table NAME --domain DOMAIN` |
16
- | `edit(entity_type="metric", id=..., secondary_tables=[...])` | `graphit kb update metric NAME --secondary-tables "TABLE1,TABLE2"` |
17
- | `edit(entity_type="metric", id=..., default_dimensions=[...])` | `graphit kb update metric NAME --default-dimensions "D1,D2"` |
18
- | `edit(entity_type="metric", id=..., topics=[...])` | `graphit kb update metric NAME --topics "TOPIC1,TOPIC2"` |
19
- | `edit(entity_type="metric", id=..., parameters=[...])` | `graphit kb update metric NAME --parameters '<json>' --parameters-file params.json` |
20
- | `delete(entity_type=..., id=...)` | `graphit kb delete <type> NAME --yes` |
21
- | `search(query="...")` | `graphit kb search "query"` |
22
- | `list(entity_type="domain")` | `graphit kb list domains` |
23
- | `read(entity_type="metric", name=...)` | `graphit kb get metric NAME` |
24
- | `kb_explore(entity_type="table", entity_name="X")` | `graphit kb explore table X` |
25
-
26
- Full CRUD parity: all agent actions have CLI equivalents.
27
-
28
- ## Asset CRUD
29
-
30
- | UI Action | Agent Tool Path |
31
- |-----------|----------------|
32
- | Create metric | `create(entity_type="metric", name=..., calculation=..., dependencies=...)` |
33
- | Create dimension | `create(entity_type="dimension", name=..., table_name=..., expression=...)` |
34
- | Create rule | `create(entity_type="rule", name=..., rule_text=...)` |
35
- | Create synonym | `create(entity_type="synonym", term=..., canonical=..., canonical_type=...)` |
36
- | Create relationship | `create(entity_type="relationship", primary_table=..., primary_column=..., related_table=..., related_column=...)` |
37
- | Edit any asset | `edit(entity_type=..., id=..., field=new_value)` |
38
- | Delete any asset | `delete(entity_type=..., id=...)` |
39
- | Toggle verified | `edit(entity_type=..., id=..., verified=true/false)` |
40
-
41
- ## Topic Management
42
-
43
- | UI Action | Agent Tool Path |
44
- |-----------|----------------|
45
- | Create topic | `create(entity_type="topic_metadata", name=..., description=...)` |
46
- | Edit topic description | `edit(entity_type="topic_metadata", id=..., description=...)` |
47
- | Delete topic metadata | `delete(entity_type="topic_metadata", id=...)` |
48
- | Toggle topic verified | `edit(entity_type="topic_metadata", id=..., verified=true/false)` |
49
- | Add topic to asset | `edit(entity_type="metric", id=..., topics=[...existing, "NEW_TOPIC"])` - append to existing list |
50
- | Remove topic from asset | `edit(entity_type="metric", id=..., topics=[...without removed])` - filter out the topic |
51
- | Move asset between topics | `edit(entity_type="metric", id=..., topics=["NEW_TOPIC"])` - replace the list |
52
- | Rename topic (cascade) | `edit(entity_type="topic_metadata", id=..., name=...)` then batch `edit` on each asset that references the old name |
53
- | Bulk reassign topics | Present plan via `ask_user`, then execute N `edit` calls on approval |
54
-
55
- ## Domain Management
56
-
57
- | UI Action | Agent Tool Path |
58
- |-----------|----------------|
59
- | Create domain | `create(entity_type="domain", name=..., description=..., color=...)` |
60
- | Edit domain | `edit(entity_type="domain", id=..., description=..., color=...)` |
61
- | Delete domain | `delete(entity_type="domain", id=...)` |
62
-
63
- ## Domain Assignment (Cascade Model)
64
-
65
- Domain is a single **home** set on the table; every asset on that table inherits it. Assets never store their own home domain - set it on the table, not the asset.
66
-
67
- | UI Action | Agent Tool Path |
68
- |-----------|----------------|
69
- | Set a table's home domain | `edit(entity_type="table", id=..., domain_id="MARKETING")` - cascades to every asset on that table |
70
- | Clear a table's home domain | `edit(entity_type="table", id=..., domain_id=None)` - its assets fall back to Uncategorized |
71
- | Reference asset onto another table | `edit(entity_type="metric", id=..., secondary_tables=["OTHER_TABLE"])` - appears under the target table as a read-only pointer (marked with `*`). Metrics/dimensions require identical columns; rules require only target existence. |
72
- | Remove a table reference | `edit(entity_type="metric", id=..., secondary_tables=[...without removed])` |
73
- | Mark a synonym cross-cutting to extra domains | `edit(entity_type="synonym", id=..., extra_domain_ids=["FINANCE"])` - adds badges + filter match, does NOT move it in the tree |
74
- | Remove cross-cutting domains from synonym | `edit(entity_type="synonym", id=..., extra_domain_ids=[])` |
75
-
76
- A table-backed asset's (metric, dimension, rule) domain is derived from its dependency tables plus any `secondary_tables`. A synonym can also carry `extra_domain_ids` for cross-cutting relevance. To re-home a whole table of assets, change `domain_id` on the table once - do not edit assets individually.
77
-
78
- ## Navigation and Discovery
79
-
80
- | UI Action | Agent Tool Path |
81
- |-----------|----------------|
82
- | Search KB | `search(query="...", types=[...])` |
83
- | Filter by topic | `search(query="...", topics=["REVENUE"])` |
84
- | Filter by domain | `search(query="...", domain="MARKETING")` - matches assets whose dependency or secondary tables are in the MARKETING domain. Synonyms also match via `extra_domain_ids`. |
85
- | List all domains | `list(entity_type="domain")` - returns every domain with name, description, color, and asset_count; includes an "Uncategorized" entry when tables with no domain exist. Report every domain it returns; do not drop ones with asset_count=0, and do not use `search` or `kb_explore` |
86
- | List all tables | `list(entity_type="table")` - returns every table with name, domain_id (null if uncategorized), and verified status. Use to see which tables are assigned to which domain, or to find uncategorized tables |
87
- | Browse by table | `search(query="...", types=["metric", "dimension"])` then filter results by table |
88
- | What depends on X? | `kb_explore(entity_type="table", entity_name="X", edge_types=["depends_on"])` |
89
- | What joins with table? | `kb_explore(entity_type="table", entity_name="X", edge_types=["joins"])` |
90
- | What's in topic X? | `kb_explore(entity_type="topic", entity_name="X", edge_types=["tagged_with"])` |
91
- | What's in domain X? | `kb_explore(entity_type="domain", entity_name="X")` |
92
-
93
- ## View Controls (UI-Only, No Agent Parity Needed)
94
-
95
- These actions are purely UI navigation state and have no agent equivalent:
96
- - Switch view mode (Tree / By Topic / By Table / Flat)
97
- - Filter dropdowns (domain, table, topic, type, verified-only)
98
- - Expand/collapse tree nodes
99
- - Drag-drop asset onto topic (maps to "Add topic to asset" above)
9
+ | Metric | `graphit kb create metric --name X --sql "<expr>" --table T` (optional `--topics "A,B"`, `--default-dimensions "D1,D2"`) |
10
+ | Dimension | `graphit kb create dimension --name X --expr "<expr>" --table T` (type auto-inferred; override with `--type` / `--output-type`) |
11
+ | Rule | `graphit kb create rule --name X --sql "<text>" --table T` (optional `--apply-on table:USERS metric:ARPU`) |
12
+ | Synonym | `graphit kb create synonym --term X --canonical Y --type metric` |
13
+ | Domain | `graphit kb create domain --name X` (optional `--color "#4DB6AC"`) |
14
+ | Topic | `graphit kb create topic --name X` |
15
+ | Relationship | `graphit kb create relationship --name X --primary-table T --primary-column C --related-table T2 --related-column C2` |
16
+
17
+ ## Update and Delete
18
+
19
+ - Update a field: `graphit kb update <type> NAME --<field> value` (e.g. `--sql`, `--expr`, `--description`, `--table`).
20
+ - Delete: `graphit kb delete <type> NAME --yes`.
21
+
22
+ ## Topic and Reference Recipes (lists REPLACE, so don't clobber)
23
+
24
+ `--topics`, `--secondary-tables`, and `--default-dimensions` replace the existing list, they do not append. To change one value without losing the others, read the current list first, then write the full intended list:
25
+
26
+ - **Add a topic**: `graphit kb get metric NAME` to read current topics, then `graphit kb update metric NAME --topics "EXISTING1,EXISTING2,NEW"`.
27
+ - **Remove a topic**: update with the list minus the one removed.
28
+ - **Reference an asset onto another table**: `graphit kb update metric NAME --secondary-tables "OTHER_TABLE"` (works on dimension and rule too). Read-only pointer marked `*`; metrics and dimensions need every referenced column to exist on the target, rules need only the table to exist.
29
+
30
+ Topics are horizontal - one topic can tag assets across many domains (see `kb-structure.md`).
31
+
32
+ ## Domain Recipes (the home is on the table and cascades)
33
+
34
+ Domain is set on the TABLE, never per asset, and cascades to every asset on it (see `kb-structure.md` for the model):
35
+
36
+ - **Re-home a whole table**: `graphit kb update table NAME --domain MARKETING` - moves every asset on that table at once. Change it once on the table, never asset by asset.
37
+
38
+ ## Navigation
39
+
40
+ To find what exists and how it connects (what depends on a table, what joins, what is in a domain or topic), use the traversal recipes in `kb-traversal.md`. Note: `graphit kb list domains` reports every domain including empty ones - report them all, do not drop ones with a zero asset count.
41
+
42
+ ## UI-Only (no CLI command)
43
+
44
+ These are platform-UI state or platform-only, not KB data the CLI changes:
45
+ - View mode (Tree / By Topic / By Table / Flat), filter dropdowns, expand / collapse, drag-drop onto a topic.
46
+ - Cross-cutting synonym domains (extra relevance beyond the home domain) are set in the platform UI - there is no CLI flag for them yet.
@@ -0,0 +1,78 @@
1
+ # KB Discovery
2
+
3
+ Consult when starting a dashboard build, or when the user references a business concept that might already exist as a KB asset. The Knowledge Base holds reusable metrics, dimensions, rules, and table schemas - using them keeps formulas consistent across dashboards and makes each new graph faster to build.
4
+
5
+ ## Domain-First Discovery
6
+
7
+ Work narrow, not broad: **domain -> data source -> assets.** Listing or searching everything is the fallback, not the default.
8
+
9
+ 1. **Scope to the domain.** Ask the user which business area this is about, or infer it and confirm. `graphit kb list domains` shows the groupings (MARKETING, PRODUCT, FINANCE, etc.) if you need to pick one.
10
+ 2. **Explore that domain.** `graphit kb explore domain <NAME>` returns the domain's data sources plus the metrics, dimensions, and rules defined on them, in one traversal. This is your working set.
11
+ 3. **Reuse the assets.** If a metric fits, read its formula with `graphit kb get metric REVENUE` and reference it by name in SQL as `{{metric:REVENUE}}`.
12
+ 4. **Handle gaps.** If the domain's data source is missing something, check `graphit kb list relationships` for a connection to another data source you can join; if the data genuinely does not exist, propose creating the asset or a new data source (see below).
13
+ 5. **Broaden only if needed.** Use `graphit kb search "<concept>"` when the domain is unclear or a concept spans domains.
14
+
15
+ If a domain has tables but no metrics or dimensions, that is the strongest signal to propose foundational assets before building any graph. If the user declines ("just build it", "skip KB"), respect it and work from the table schema.
16
+
17
+ ## Metric vs Dimension
18
+
19
+ | Property | Metric | Dimension |
20
+ |---|---|---|
21
+ | Formula | Aggregation required (SUM, COUNT, AVG, MIN, MAX) | Row-level only (no aggregates) |
22
+ | Table scope | Can reference multiple tables | Exactly one table |
23
+ | Purpose | Measures - what you count or sum | Grouping axes - how you slice |
24
+ | Example | `SUM(ORDERS.AMOUNT)` | `DATE_TRUNC('month', EVENTS.EVENT_TS)` |
25
+ | Invalid | `ORDERS.AMOUNT` (no aggregate) | `SUM(EVENTS.DURATION)` (has aggregate) |
26
+
27
+ ## When to Propose KB Asset Creation
28
+
29
+ | Signal | Propose | Why |
30
+ |---|---|---|
31
+ | User requests a business metric (revenue, DAU, conversion rate) with no KB match | Metric | Reusable formula across dashboards |
32
+ | User groups by a derived expression (date bucket, category mapping, JSON extraction) | Dimension | Consistent grouping logic |
33
+ | User describes a business rule ("active = logged in within 30 days") | Rule | Applied automatically to future queries |
34
+ | User uses a business term not in KB ("GMV", "churn", "ARPU") | Synonym | Maps colloquial terms to a defined metric |
35
+
36
+ Propose creation as the default path, in plain language: "Your KB doesn't have a revenue metric yet. I'd recommend creating one first - then it's reusable across every dashboard with a consistent formula, and I'll build the graph using it. Sound good?"
37
+
38
+ ## Naming Conventions
39
+
40
+ All KB assets are UPPER_SNAKE_CASE (auto-sanitized on write):
41
+
42
+ | Pattern | Example | Use when |
43
+ |---|---|---|
44
+ | `TOTAL_*` | `TOTAL_REVENUE`, `TOTAL_ORDERS` | Sum aggregations |
45
+ | `AVG_*` | `AVG_ORDER_VALUE` | Average metrics |
46
+ | `COUNT_*` | `COUNT_ACTIVE_USERS` | Count metrics |
47
+ | `*_RATE` | `CONVERSION_RATE`, `CHURN_RATE` | Ratios / percentages |
48
+
49
+ ## Formula Syntax
50
+
51
+ Metrics reference `TABLE.COLUMN` with UPPERCASE naming:
52
+
53
+ ```sql
54
+ -- Metric formulas (aggregation required)
55
+ SUM(ORDERS.AMOUNT)
56
+ COUNT(DISTINCT EVENTS.USER_ID) WHERE EVENTS.EVENT_TS >= DATEADD(day, -30, CURRENT_DATE)
57
+ SUM(ORDERS.REVENUE) / NULLIF(SUM(ORDERS.COST), 0)
58
+
59
+ -- Dimension formulas (no aggregates)
60
+ EVENTS.PLATFORM
61
+ DATE_TRUNC('month', EVENTS.EVENT_TS)
62
+ CASE WHEN USERS.AGE >= 18 THEN 'adult' ELSE 'minor' END
63
+ ```
64
+
65
+ Use CASE WHEN for conditionals (Snowflake has no FILTER WHERE). Always guard division with NULLIF.
66
+
67
+ When creating assets, pass `--topics` to tag the business concept (e.g. `--topics "ACQUISITION"`; check existing topic names first), and `--default-dimensions` on a metric to declare its natural grouping axes. Dimension `output_type` / `semantic_type` are auto-inferred from the column schema - override with `--type` / `--output-type` only when the inference is wrong (e.g. a CASE expression that outputs a category from a numeric column). Full command reference: `kb-actions.md`.
68
+
69
+ ## Reuse Over Reinvention
70
+
71
+ When a dashboard has several graphs sharing a concept, propose ONE KB asset instead of repeating the formula:
72
+
73
+ - 3 graphs all using `SUM(ORDERS.AMOUNT)` -> one `TOTAL_REVENUE` metric.
74
+ - 2 graphs grouping by `DATE_TRUNC('month', TS)` -> one `MONTHLY` dimension.
75
+
76
+ For the same concept on a sibling table with identical columns, **reference** it rather than duplicating: `graphit kb update metric TOTAL_REVENUE --secondary-tables "ORDERS_12M"`. This is a pointer, not a copy - edits propagate to all placements, and referenced placements show a `*` in the KB tree. Metrics and dimensions require every referenced column to exist on the target table (checked on save); rules only require the table to exist. Suggest it when the user has sibling sources (e.g. 6M and 12M windows) or asks to "make X available on Y".
77
+
78
+ For the KB graph model and structural questions (the vertical domain -> DS -> assets home vs horizontal topics, why assets appear where they do), see `kb-structure.md`; for graph-traversal queries, see `kb-traversal.md`. For the full create / update / delete command matrix, see `kb-actions.md`.
@@ -0,0 +1,64 @@
1
+ # KB Structure
2
+
3
+ How the Knowledge Base is organized, and how to answer structural questions about it. The KB is a labeled property graph: assets carry tags that place them in a tree, but the underlying structure is a graph with typed edges. Answer structural questions in business-friendly language, grounded in actual KB state (verify with `graphit kb get` / `graphit kb explore` when the question names a specific asset).
4
+
5
+ ## Node Types
6
+
7
+ | Type | Description |
8
+ |------|-------------|
9
+ | metric | Aggregation formula (SUM, COUNT, AVG) that computes a business KPI |
10
+ | dimension | Row-level SQL expression on one table, used for grouping or filtering |
11
+ | rule | Free-text business constraint, optionally scoped to one or more tables |
12
+ | synonym | Maps a business term to a canonical metric, dimension, or column |
13
+ | table | Physical data location in the warehouse, with typed columns |
14
+ | topic | Business-concept tag applied to assets (e.g., REVENUE, ACQUISITION) |
15
+ | domain | High-level business area (e.g., MARKETING, SALES, PRODUCT) |
16
+ | relationship | Documented JOIN pattern between two tables |
17
+ | memory | Org-level context notes, always global scope |
18
+
19
+ ## Edge Types
20
+
21
+ | Edge | From | To | Meaning |
22
+ |------|------|----|---------|
23
+ | depends_on | metric, dimension | table | Asset's SQL references columns in this table |
24
+ | tagged_with | metric, dimension, rule, synonym | topic | Asset carries this business-concept tag |
25
+ | in_domain | table, metric, dimension, rule, synonym | domain | A table has one home domain; assets inherit it from their primary table, plus any cross-cutting extras |
26
+ | joins | relationship | table, table | Two tables have a documented JOIN on specific columns |
27
+ | references | rule, synonym | table, column | Rule or synonym references a specific table or column name |
28
+
29
+ ## How Membership Works
30
+
31
+ Two different axes organize the KB - keep them separate:
32
+
33
+ - **Vertical: the home (domain -> data source -> assets).** This is containment, and it cascades. An asset has ONE home domain, inherited from its primary table (the data source its SQL reads). You set the domain on the **table** - `graphit kb update table NAME --domain MARKETING` - and every asset on that table inherits it; the asset carries no domain tag of its own. To move an asset's home, change its table's domain. Domain-first discovery walks this axis downward.
34
+ - **Horizontal: the concept (topics).** Topics cut ACROSS domains. The same topic (e.g., RETENTION) can tag assets that live in different domains, grouping them by business meaning regardless of where they sit in the vertical hierarchy. An asset can carry several topics at once - `graphit kb update metric NAME --topics "REVENUE,RETENTION"` - and a topic never moves an asset's home. When a concept spans domains, the horizontal (by-topic) view is how you find everything about it.
35
+
36
+ Other placements layered on top:
37
+ - **Multiple tables**: a metric or dimension can depend on several tables (e.g., a JOIN across ORDERS and CUSTOMERS) and appears under each. Table dependency is structural - derived from the SQL, never tagged manually.
38
+ - **Referencing (`secondary_tables`)**: an asset can be referenced onto additional tables; it shows under the target with a `*` suffix and links back to the original, and stays editable only from its home table. Table-backed assets derive domain membership from all their tables (primary + `secondary_tables`).
39
+ - **Cross-cutting domains**: synonyms can carry extra domains in `extra_domain_ids` for relevance beyond their home - badges that match domain filters without moving the asset in the tree.
40
+
41
+ Domains are coarse and few (broad business areas); topics are finer and more numerous. A single domain like MARKETING typically spans topics such as ACQUISITION, ATTRIBUTION, and CAMPAIGN_PERFORMANCE.
42
+
43
+ ## Tree Rendering Order
44
+
45
+ The default tree renders Domain > Table > Topic > Asset, with each table under its one home domain. This is a visualization choice; the model also supports Topic > Table or a flat list. When the user asks "where is X?", an asset lives under its primary table's home domain - report that, plus any domains from its `secondary_tables` placements and its topics.
46
+
47
+ ## Answering Structural Questions (Tier 1)
48
+
49
+ Structural questions have direct answers - give them in business terms:
50
+
51
+ | Question | Answer pattern |
52
+ |---|---|
53
+ | What is a topic / domain / table / relationship? | Use the Node Types descriptions above, in plain language |
54
+ | Why is [metric] under [table]? | Its SQL depends on that table's columns (or it is referenced there via `secondary_tables`, shown as `*`) |
55
+ | Why is [asset] in [topic]? | Someone tagged it - topics are manual, not derived. Check its topics list |
56
+ | Why is [asset] in [domain]? | Its primary table's domain cascades to it; it was not tagged directly |
57
+ | What is a relationship? | A documented JOIN between two tables (which columns, which join type) used to build correct cross-table SQL |
58
+ | What is memory? | Org-level context notes (goals, terminology, conventions), global scope, shown at the tree root |
59
+
60
+ Verify against actual KB state when the question names a specific asset (`graphit kb get`, `graphit kb explore`).
61
+
62
+ ## Semantic Questions (Tier 2 - Different Handling)
63
+
64
+ Questions about what a business concept MEANS at this org ("what does revenue mean for us?", "how do we define an active user?") are semantic, not structural. For these: search the KB for the relevant metric or rule, read its description and calculation, and present what the KB says - do not interpret or extend it from general knowledge. The KB is the source of truth for this org's definitions.
@@ -1,61 +1,44 @@
1
1
  # KB Traversal - Worked Examples
2
2
 
3
- Common graph queries and which tool to use for each. Agent tool names map to CLI commands: `search` = `graphit kb search`, `read` = `graphit kb get`, `list` = `graphit kb list`, `kb_explore` = `graphit kb explore`.
3
+ Common questions about the KB graph and the CLI command for each. Three commands do most of the work: `graphit kb search "<query>"` (semantic discovery across all types, optional `--type`), `graphit kb get <type> NAME` (full details of one asset), and `graphit kb explore <type> NAME` (walk the graph around one entity).
4
4
 
5
- ## Tool Selection Guide
5
+ ## Which Command
6
6
 
7
- | Question Shape | Best Tool | Why |
8
- |----------------|-----------|-----|
9
- | Semantic ("find things like X") | `search` | Embedding-based discovery across all asset types |
10
- | Pointed retrieval ("get metric Y") | `read` | Direct fetch by exact name, returns full details |
11
- | Topic/domain filter ("metrics in REVENUE") | `search` with topics/domain params | Filtered discovery, intersects with query |
12
- | Connectivity ("what depends on X") | `kb_explore` | Graph walk from a starting entity, returns typed edges |
13
- | Joins specifically | `kb_explore` with edge_types=["joins"] | Targeted relationship traversal |
14
- | Multi-hop ("what's 2 hops from X") | `kb_explore` with max_depth=2 | Recursive walk, capped at 3 hops |
7
+ | Question | Command |
8
+ |---|---|
9
+ | Find things like X (semantic) | `graphit kb search "X"` (add `--type metric` to narrow) |
10
+ | Get one asset's full details | `graphit kb get metric NAME` |
11
+ | What connects to X (dependencies, joins, topics, domain) | `graphit kb explore <type> NAME` |
12
+ | What is inside a domain or topic | `graphit kb explore domain NAME` / `graphit kb explore topic NAME` |
13
+
14
+ `graphit kb explore` returns the whole neighborhood in one call and has no edge-type or depth flags - the response already includes dependents, joins, topics, and domain together, so read the part you need instead of chaining calls.
15
15
 
16
16
  ## Common Queries
17
17
 
18
18
  ### "Find all revenue metrics"
19
- Use `search` with a topic filter to get metrics tagged with the REVENUE topic. If the user means revenue conceptually (not a specific topic), use a semantic search query instead.
19
+ `graphit kb search "revenue" --type metric` for semantic matches. To get the assets tagged with a specific topic, `graphit kb explore topic REVENUE`.
20
20
 
21
- ### "What depends on ORDERS table?"
22
- Use `kb_explore` starting from the table, filtering to depends_on edges. Returns all metrics and dimensions whose SQL references ORDERS columns.
21
+ ### "What depends on the ORDERS table?"
22
+ `graphit kb explore table ORDERS` - the response lists every metric and dimension whose SQL references ORDERS columns, plus the relationships it joins through.
23
23
 
24
24
  ### "What joins with MARKETING_UA_DS?"
25
- Use `kb_explore` starting from the table, filtering to joins edges. Returns relationship entities that document JOINs involving that table.
25
+ `graphit kb explore table MARKETING_UA_DS`, then read the relationships in the response (the documented JOINs involving that table).
26
26
 
27
27
  ### "What domains do we have?"
28
- Use `list` with the domain entity type (`list(entity_type="domain")`). It returns every domain with its description and asset_count, plus an "Uncategorized" entry when tables with no domain exist. Report all of them, including ones with asset_count=0. Do NOT use `search` (domain is not a searchable type) or `kb_explore` (it traverses from one named domain - a wildcard like "*" returns nothing).
29
-
30
- ### "What's left in uncategorized?"
31
- Two paths: (1) `list(entity_type="table")` and filter for `domain_id=null` to find unassigned tables, then `kb_explore(entity_type="domain", entity_name="Uncategorized")` to get all tables and assets in the pseudo-domain; or (2) just use `kb_explore(entity_type="domain", entity_name="Uncategorized")` directly to get everything.
28
+ `graphit kb list domains` - returns every domain with its description and asset count, plus an "Uncategorized" entry when tables have no domain. Report all of them, including empty ones. Domain is not a searchable type, so `graphit kb search` will not surface domains.
32
29
 
33
30
  ### "Show me everything in the MARKETING domain"
34
- Use `kb_explore` starting from the domain. Returns tables (via in_domain edge) and all assets within those tables (via depends_on edges at depth 2).
31
+ `graphit kb explore domain MARKETING` - returns the domain's tables and the assets on them.
35
32
 
36
33
  ### "What topics does ARPU_D1 belong to?"
37
- Use `kb_explore` starting from the metric, filtering to tagged_with edges. Returns all topic nodes the metric is tagged with.
38
-
39
- ### "Find metrics in same table as ARPU_D1 but different topic"
40
- Two-step query:
41
- 1. Use `kb_explore` on ARPU_D1 to find its table(s) and topic(s)
42
- 2. Use `search` filtered to that table's metrics, then compare topics in the results
43
-
44
- ### "What domains have retention metrics?"
45
- Use `search` with topics=["RETENTION"] and types=["metric"]. A metric's domain is derived from its dependency tables and secondary_tables - there is no domain field directly on the metric. Group the results by that effective domain. To get the home domain for one metric, `kb_explore` from the metric with edge_types=["in_domain"] returns it.
46
-
47
- ### "Show me the full graph around table X"
48
- Use `kb_explore` with max_depth=2 and no edge_type filter. Returns the table's domain, all dependent assets, all topics those assets carry, and all relationships the table participates in.
34
+ `graphit kb explore metric ARPU_D1` - the response includes its topics, along with its tables, dimensions, and home domain.
49
35
 
50
- ## Depth Guidelines
36
+ ### "What's left uncategorized?"
37
+ `graphit kb explore domain Uncategorized` - returns the tables with no home domain and their assets.
51
38
 
52
- | Depth | Use When |
53
- |-------|----------|
54
- | 1 (default) | Direct neighbors only. Best for most questions. |
55
- | 2 | When you need one extra hop (e.g., table's assets AND their topics) |
56
- | 3 (max) | Full neighborhood. Use sparingly - can return many results for connected tables. |
39
+ ## Reading the Results
57
40
 
58
- Start at depth 1. Only increase if the user's question requires it or the initial results are insufficient.
41
+ A single `graphit kb explore` call returns one entity's full neighborhood, so it usually answers the question on its own - scan the response rather than chaining many calls. Start there, and only run a second command if the first response does not contain what you need.
59
42
 
60
43
  ## Presenting KB Results
61
44
 
@@ -69,7 +52,6 @@ Start at depth 1. Only increase if the user's question requires it or the initia
69
52
  | **CPI** | **MARKETING_UA** | `SUM(spend)/SUM(installs)` | - |
70
53
  | **ARPU** | **MARKETING_UA** | `SUM(revenue)/COUNT(...)` | DAY |
71
54
  | **RETENTION** | **PLAYER_QUALITY** | `COUNT(CASE WHEN ...)` | DAY |
72
- | ... | | | |
73
55
 
74
56
  4 parameterized (need `(DAY=N)` syntax), 8 pre-baked.
75
57
  ~~~
@@ -115,7 +97,6 @@ Adapt fields per type. Rules: content, constraints, apply-on, override policy. D
115
97
  - **Related dimensions (8):**
116
98
  - **MEDIA_SOURCE** - `media_source` (categorical)
117
99
  - **CAMPAIGN_NAME** - `campaign_name` (categorical)
118
- - ...
119
100
  - **Rules:** **EXCLUDE_ORGANIC** (filters organic installs)
120
101
  - **Domain:** MARKETING
121
102
  ~~~
@@ -77,8 +77,7 @@ deck.slide({
77
77
  html: `
78
78
  <h2>Live Data</h2>
79
79
  <div data-graphit-id="spend-chart" data-graphit-label="Ad Spend"
80
- data-graphit-kb="metric:TOTAL_AD_SPEND,table:MARKETING_UA"
81
- data-graphit-sql="SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) AS spend FROM MARKETING_UA_DS GROUP BY 1 ORDER BY spend DESC LIMIT 6"
80
+ data-graphit-sql="SELECT {{dim:MEDIA_SOURCE_DIMENSION}} AS source, {{metric:TOTAL_AD_SPEND}} AS spend FROM MARKETING_UA_DS GROUP BY 1 ORDER BY spend DESC LIMIT 6"
82
81
  data-graphit-ds="ds_abc123">
83
82
  <div id="chart1" class="gh-loading">
84
83
  <div class="gh-loading-overlay"><svg class="gh-loading-spin" width="24" height="24" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="#e5e5e5" stroke-width="2.5"/><path d="M12 2a10 10 0 0 1 10 10" stroke="#4DB6AC" stroke-width="2.5" stroke-linecap="round"/></svg></div>
@@ -1,90 +0,0 @@
1
- # KB-Aware Planning
2
-
3
- > Load when: planning graphs that involve metrics (aggregations) or dimensions (grouping/filtering columns), or when the user references a business concept that might already exist as a KB asset.
4
-
5
- Before writing inline SQL with raw aggregations or column references, check whether a KB asset already defines the concept. Using KB assets ensures consistent formulas across dashboards and makes future graphs faster to create.
6
-
7
- ## KB-First Discovery
8
-
9
- Before proposing graphs with inline aggregations or custom column expressions:
10
-
11
- 1. Check the KB Overview in the system prompt for existing metrics and dimensions
12
- 2. If the KB Overview shows assets that match the user's request, reference them by name
13
- 3. If no matching asset exists, recommend creating it first, then build the graph referencing the new asset
14
-
15
- If the KB Overview shows tables but no metrics or dimensions, this is the strongest signal to suggest KB asset creation. Propose foundational metrics and dimensions based on the table schema before planning any graphs. If the user explicitly declines ("just build it", "skip KB"), respect that and work from table schema.
16
-
17
- ## When to Propose KB Asset Creation
18
-
19
- | Signal | Proposed asset | Why |
20
- |--------|---------------|-----|
21
- | User requests a business metric (revenue, DAU, conversion rate) with no KB match | Metric | Reusable formula across dashboards |
22
- | User groups by a derived expression (date bucket, category mapping, JSON extraction) | Dimension | Consistent grouping logic |
23
- | User describes a business rule ("active means logged in within 30 days") | Rule | Applied automatically to future queries |
24
- | User uses a business term not in KB ("GMV", "churn", "ARPU") | Synonym | Maps colloquial terms to defined metrics |
25
-
26
- Propose creation as the default path: "Your KB doesn't have a revenue metric yet. I'd recommend creating one first - that way it's reusable across all your dashboards with a consistent formula. Then I'll build the graph using it. Sound good?"
27
-
28
- ## Metric vs Dimension Distinction
29
-
30
- | Property | Metric | Dimension |
31
- |----------|--------|-----------|
32
- | Formula type | Aggregation required (SUM, COUNT, AVG, MIN, MAX) | Row-level only (no aggregates) |
33
- | Table scope | Can reference multiple tables | Exactly one table |
34
- | Purpose | Measures (what you count/sum) | Grouping axes (how you slice) |
35
- | Example | `SUM(ORDERS.AMOUNT)` | `DATE_TRUNC('month', EVENTS.EVENT_TS)` |
36
- | Invalid example | `ORDERS.AMOUNT` (no aggregate) | `SUM(EVENTS.DURATION)` (has aggregate) |
37
-
38
- ## Topics
39
-
40
- When creating any KB asset, consider which business topic it belongs to and pass `topics` (e.g., `["ACQUISITION"]`, `["MONETIZATION"]`). Check the KB Overview for existing topic names first to avoid duplicates. UPPER_SNAKE_CASE.
41
-
42
- ## Dimension Type Inference
43
-
44
- When creating dimensions, `output_type` and `semantic_type` are auto-inferred from the table's column schema for simple column references (e.g., a DATE column becomes `output_type=DATE`, `semantic_type=temporal`). Only override with `--type` / `--output-type` when the inferred type is wrong (e.g., a CASE expression that produces categorical output from a numeric column).
45
-
46
- ## Metric Default Dimensions
47
-
48
- When creating metrics, pass `--default-dimensions "D1,D2"` to declare which dimensions the metric naturally groups by. This populates the "USED BY METRICS" section on dimension detail views and helps users discover natural slicing axes.
49
-
50
- ## Reuse Over Reinvention
51
-
52
- When planning multi-graph dashboards, identify shared concepts across graphs and propose KB assets for them:
53
-
54
- - If 3 graphs all use `SUM(ORDERS.AMOUNT)`, propose a TOTAL_REVENUE metric once
55
- - If 2 graphs group by `DATE_TRUNC('month', TS)`, propose a MONTHLY dimension once
56
- - Reference the KB asset by name in subsequent graphs instead of repeating the formula
57
-
58
- ## Cross-Table Referencing
59
-
60
- When a metric or dimension already exists on one table and the user wants the same concept on a sibling table with identical columns, **reference it** instead of creating a duplicate:
61
-
62
- `edit(entity_type="metric", id="TOTAL_REVENUE", secondary_tables=["ORDERS_12M"])`
63
-
64
- - Metrics/dimensions: the target table must contain every column the formula references (validated on save).
65
- - Rules: the target table just needs to exist (no column check).
66
- - This is a pointer, not a copy. Edits propagate to all placements.
67
- - In the KB tree, referenced placements appear with a `*` suffix and link back to the original.
68
-
69
- Suggest referencing when: the user has sibling data sources with the same schema (e.g., 6M and 12M windows), or asks to "make X available on Y table".
70
-
71
- > For metric formula syntax, aggregation patterns, and WHERE clause rules, see `kb_authoring/references/formula-syntax.md`. For creating KB assets, hand off to `kb_authoring`.
72
-
73
- ## KB Graph Navigation
74
-
75
- For structural questions about the KB (what is a topic, what depends on X, how do domains work), graph traversal, or KB organization requests, activate the `kb_navigation` shared skill. It provides:
76
-
77
- - `kb_explore` tool for graph traversal (what depends on X, what joins with Y)
78
- - Structural explanations grounded in actual KB data
79
- - Full action parity with the manual KB page UI
80
- - Topic and domain filtering via `search` tool
81
-
82
- **When to activate kb_navigation vs stay in current skill:**
83
-
84
- | User Intent | Activate kb_navigation? |
85
- |-------------|------------------------|
86
- | "What is a topic?" or "How do domains work?" | Yes - structural question |
87
- | "What depends on this table?" | Yes - graph traversal |
88
- | "Organize my KB" or "Clean up topics" | Yes - KB management |
89
- | "Show me revenue metrics for my graph" | No - use search + read, stay in current skill |
90
- | "Create a metric called X" | No - use kb_expert or kb_authoring |
@@ -1,50 +0,0 @@
1
- # KB Explanation - Structural Q&A Patterns
2
-
3
- Tier 1 structural questions about KB concepts. Answer these using the patterns below, grounded in actual KB data when possible. Use business-friendly language - no implementation details.
4
-
5
- ## Tier 1: Structural (In Scope)
6
-
7
- Answer directly using these patterns. Always verify against actual KB state when the question involves specific entities.
8
-
9
- ### What is a topic?
10
- A topic is a business concept tag you apply to metrics, dimensions, rules, and synonyms. Examples: REVENUE, ACQUISITION, RETENTION. Any asset can have multiple topics. Topics help organize the KB by business meaning rather than physical table location.
11
-
12
- ### What is a domain?
13
- A domain is a high-level business area like MARKETING, SALES, or PRODUCT. Domains are broader than topics - think of them as departments or business units. A domain is set on a table, and every metric, dimension, and rule built on that table belongs to that domain automatically. An org typically has 3-10 domains and 10-30+ topics.
14
-
15
- ### How do domains and topics differ?
16
- The difference is granularity and how they attach:
17
- - A **domain** is coarse (5-10 per org: MARKETING, FINANCE, PRODUCT) and is set on the table. Each asset has one home domain, inherited from the table its data comes from. An asset can also be marked relevant to a few extra domains when it genuinely spans areas.
18
- - A **topic** is fine (10-30+ per org: REVENUE, ACQUISITION, RETENTION) and is tagged directly on assets. An asset can carry several topics at once.
19
-
20
- A single domain like MARKETING might contain topics like ACQUISITION, ATTRIBUTION, and CAMPAIGN_PERFORMANCE.
21
-
22
- ### What is a table in the KB?
23
- A table represents a physical data location in the warehouse. Metrics and dimensions depend on tables because their SQL formulas reference table columns. Tables are structural, not tags - the dependency is derived from the SQL, not manually assigned.
24
-
25
- ### What is a relationship?
26
- A relationship documents a JOIN between two tables. It specifies which columns connect them and what kind of join to use. When the agent writes SQL across multiple tables, it uses relationships to build correct JOINs.
27
-
28
- ### Why is [metric] under [table]?
29
- A metric appears under a table because the metric's SQL formula depends on columns in that table. Check the metric's dependencies to see exactly which columns it uses. A metric can depend on multiple tables - it appears under each one, with the first listed as primary. An asset can also appear under a table via **referencing** (`secondary_tables`) - this shows as a `*` marker in the tree and means the asset was made available on that table as a read-only pointer. The original lives on its home table.
30
-
31
- ### Why is [asset] in [topic]?
32
- An asset appears under a topic because someone (user, agent, or scanner) tagged it with that topic. Topics are manually assigned tags, not automatically derived. Check the asset's topics list to see all its topic assignments.
33
-
34
- ### Why is [asset] in [domain]?
35
- An asset's domain comes from the table its data lives in: whoever set the table's domain, every asset on that table inherits it. So a metric is in MARKETING because its primary table is a MARKETING table, not because the metric itself was tagged. If an asset genuinely spans areas, it can also be marked relevant to extra domains, which show as badges and match domain filters without moving it in the tree. To change a metric's home domain, change the domain on its table.
36
-
37
- ### How do tables and topics differ?
38
- Tables are physical (where the data lives in the warehouse). Topics are conceptual (what the data is about). One table can have assets tagged with many different topics, and one topic can span assets across multiple tables.
39
-
40
- ### What is memory?
41
- Memory stores org-level context notes - background information about the organization, its goals, terminology, or conventions. Memory is global (not scoped to any table, domain, or topic) and appears at the root of the KB tree.
42
-
43
- ## Tier 2: Semantic (Out of Scope for This Skill)
44
-
45
- Questions about what a specific business concept means at this organization (e.g., "what does revenue mean for us?", "how do we define an active user?") are semantic questions, not structural ones. For these:
46
- 1. Search the KB for the relevant metric or rule
47
- 2. Read its description and calculation
48
- 3. Present what the KB says - do not interpret or extend it
49
-
50
- Do not attempt to answer semantic questions from general knowledge. The KB is the source of truth for this org's definitions.
@@ -1,63 +0,0 @@
1
- # KB Exploration
2
-
3
- Consult when starting a dashboard build. The Knowledge Base contains reusable metrics, dimensions, rules, and table schemas that ensure consistent formulas across dashboards.
4
-
5
- ## KB-First Discovery
6
-
7
- Before writing raw SQL with inline aggregations or column references:
8
-
9
- 1. Understand organization: `graphit kb list domains` shows business-area groupings (MARKETING, PRODUCT, FINANCE, etc.)
10
- 2. List what exists: `graphit kb list metrics`, `graphit kb list dimensions`, `graphit kb list rules`
11
- 3. If a KB metric matches the user's request, use its formula: `graphit kb get metric REVENUE`
12
- 4. If no match exists, consider whether to build the dashboard from raw columns or suggest KB asset creation first
13
- 5. Explore relationships: `graphit kb explore metric REVENUE` shows which tables and dimensions connect to a metric
14
-
15
- ## Metric vs Dimension
16
-
17
- | Property | Metric | Dimension |
18
- |---|---|---|
19
- | Formula | Aggregation required (SUM, COUNT, AVG, MIN, MAX) | Row-level only (no aggregates) |
20
- | Table scope | Can reference multiple tables | Exactly one table |
21
- | Purpose | Measures - what you count/sum | Grouping axes - how you slice |
22
- | Example | `SUM(ORDERS.AMOUNT)` | `DATE_TRUNC('month', EVENTS.EVENT_TS)` |
23
- | Invalid | `ORDERS.AMOUNT` (no aggregate) | `SUM(EVENTS.DURATION)` (has aggregate) |
24
-
25
- ## Naming Conventions
26
-
27
- All KB assets use UPPER_SNAKE_CASE. The names are auto-sanitized:
28
-
29
- | Pattern | Example | Use when |
30
- |---|---|---|
31
- | `TOTAL_*` | `TOTAL_REVENUE`, `TOTAL_ORDERS` | Sum aggregations |
32
- | `AVG_*` | `AVG_ORDER_VALUE` | Average metrics |
33
- | `COUNT_*` | `COUNT_ACTIVE_USERS` | Count metrics |
34
- | `*_RATE` | `CONVERSION_RATE`, `CHURN_RATE` | Ratios/percentages |
35
-
36
- ## When to Suggest KB Asset Creation
37
-
38
- | Signal | Propose |
39
- |---|---|
40
- | User requests a business metric with no KB match | Metric - reusable formula |
41
- | User groups by a derived expression | Dimension - consistent grouping |
42
- | User describes a business rule ("active = logged in within 30d") | Rule - applied to future queries |
43
- | User uses a business term not in KB | Synonym - maps colloquial to defined |
44
-
45
- ## Formula Syntax
46
-
47
- Metrics require `TABLE.COLUMN` references with UPPERCASE naming:
48
-
49
- ```sql
50
- -- Valid metric formulas
51
- SUM(ORDERS.AMOUNT)
52
- COUNT(DISTINCT EVENTS.USER_ID) WHERE EVENTS.EVENT_TS >= DATEADD(day, -30, CURRENT_DATE)
53
- SUM(ORDERS.REVENUE) / NULLIF(SUM(ORDERS.COST), 0)
54
-
55
- -- Valid dimension formulas (no aggregates)
56
- EVENTS.PLATFORM
57
- DATE_TRUNC('month', EVENTS.EVENT_TS)
58
- CASE WHEN USERS.AGE >= 18 THEN 'adult' ELSE 'minor' END
59
- ```
60
-
61
- Conditionals use CASE WHEN (not FILTER WHERE - Snowflake doesn't support it). Always guard division with NULLIF.
62
-
63
- For deeper coverage, consult: `kb-graph-structure.md` (graph model), `kb-awareness.md` (KB-first planning), `kb-explanation.md` (structural Q&A), `parameterized-metrics.md` (metric templates), `kb-traversal.md` (graph queries), `kb-actions.md` (full CRUD).
@@ -1,55 +0,0 @@
1
- # KB Graph Structure
2
-
3
- The Knowledge Base is a labeled property graph. Assets carry tags that place them in a tree, but the underlying structure is a graph with typed edges.
4
-
5
- ## Node Types
6
-
7
- | Type | Count Range | Description |
8
- |------|-------------|-------------|
9
- | metric | 10-200+ | Aggregation formula (SUM, COUNT, AVG) that computes a business KPI |
10
- | dimension | 20-500+ | Row-level SQL expression on one table, used for grouping or filtering |
11
- | rule | 5-100+ | Free-text business constraint, optionally scoped to one or more tables |
12
- | synonym | 10-100+ | Maps a business term to a canonical metric, dimension, or column |
13
- | table | 2-50+ | Physical data location in the warehouse, with typed columns |
14
- | topic | 5-30+ | Business-concept tag applied to assets (e.g., REVENUE, ACQUISITION) |
15
- | domain | 3-10 | High-level business area (e.g., MARKETING, SALES, PRODUCT) |
16
- | relationship | 2-30+ | Documented JOIN pattern between two tables |
17
- | memory | 1-5 | Org-level context notes, always global scope |
18
-
19
- ## Edge Types
20
-
21
- | Edge | From | To | Meaning |
22
- |------|------|----|---------|
23
- | depends_on | metric, dimension | table | Asset's SQL references columns in this table |
24
- | tagged_with | metric, dimension, rule, synonym | topic | Asset carries this business-concept tag |
25
- | in_domain | table, metric, dimension, rule, synonym | domain | A table has one home domain; assets inherit that home from their primary table, plus any cross-cutting extras |
26
- | joins | relationship | table, table | Two tables have a documented JOIN on specific columns |
27
- | references | rule, synonym | table, column | Rule or synonym references a specific table or column name |
28
-
29
- ## Multi-Membership Semantics
30
-
31
- Tables and topics are multi-valued; domain works differently - a single **home** that cascades:
32
-
33
- - A metric can depend on **multiple tables** (e.g., JOIN across ORDERS and CUSTOMERS), and appears under each.
34
- - An asset has **one home domain**, inherited from its primary table (the first table its SQL depends on). The asset does not carry its own domain tag - set the domain on the table and every asset on that table inherits it.
35
- - An asset can carry **multiple topics** (e.g., ARPU tagged with both REVENUE and MONETIZATION).
36
- - An asset can be **referenced** onto additional tables via `secondary_tables`. Referenced placements appear in the tree under the target table with a `*` suffix and link back to the original. The asset is editable only from its home table.
37
- - Synonyms can carry **cross-cutting domains** in `extra_domain_ids` for relevance beyond the home domain. Table-backed assets (metrics, dimensions, rules) derive domain membership entirely from their tables.
38
-
39
- For multiple tables, the tree shows the asset under each table with a link icon. For domain, the tree places each table (and its assets) under its one home domain only - cross-cutting extras are badges, not duplicate placements.
40
-
41
- ## Tags vs Structure
42
-
43
- | Concept | Type | Cardinality | Notes |
44
- |---------|------|-------------|-------|
45
- | Domain | Home (on table) + secondary_tables | One home per asset (from its table); domain derived from all tables | Coarser grouping (~5-10 per org). Home cascades from the primary table; `secondary_tables` extends reach; synonyms use `extra_domain_ids` |
46
- | Topic | Pure tag | Any (typically 1-3) | Finer grouping (~10-30+ per org) |
47
- | Table | Structural dependency | Any (typically 1-2) | Based on SQL dependencies, not manual tagging |
48
- | Relationship | First-class edge | Exactly 2 tables | Explicit JOIN documentation |
49
- | Memory | Global | N/A | Not scoped to any table, domain, or topic |
50
-
51
- **Key insight for traversal:** Topics are multi-valued tags on assets (agent adds/removes via `edit`). Domain is a single home set on the **table** (`edit(entity_type="table", domain_id=...)`), which cascades to every asset on that table. Table-backed assets derive domain membership from all their tables (primary + `secondary_tables`). Synonyms use `extra_domain_ids` for cross-cutting domain relevance. Table dependencies are structural (derived from SQL). Relationships are explicit edges between tables.
52
-
53
- ## Tree Rendering Order
54
-
55
- The default tree renders as: Domain > Table > Topic > Asset, where each table sits under its one home domain. This is a visualization choice; the data model also supports other groupings (Topic > Table, flat list). When the user asks "where is X?", an asset lives under its primary table's home domain - report that, plus any domains from its `secondary_tables` placements and its topics.