@graphit/cli 0.1.18 → 0.1.21
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/dist/commands/dashboard.js +1 -1
- package/dist/commands/dashboard.js.map +1 -1
- package/package.json +1 -1
- package/skills/graphit/SKILL.md +35 -22
- package/skills/graphit/cursor/graphit-data-sources.mdc +31 -0
- package/skills/graphit/cursor/graphit-presentations.mdc +56 -0
- package/skills/graphit/cursor/graphit-sql-reference.mdc +2 -0
- package/skills/graphit/graphit.mdc +7 -8
- package/skills/graphit/references/chart-patterns.md +2 -13
- package/skills/graphit/references/chart-selection.md +22 -17
- package/skills/graphit/references/dashboard-planning.md +10 -0
- package/skills/graphit/references/data-sources.md +25 -0
- package/skills/graphit/references/domain-lenses.md +2 -0
- package/skills/graphit/references/graphit-style.md +11 -0
- package/skills/graphit/references/kb-actions.md +43 -96
- package/skills/graphit/references/kb-discovery.md +78 -0
- package/skills/graphit/references/kb-structure.md +64 -0
- package/skills/graphit/references/kb-traversal.md +21 -40
- package/skills/graphit/references/presentations.md +142 -0
- package/skills/graphit/references/sql-reference.md +2 -0
- package/skills/graphit/references/kb-awareness.md +0 -90
- package/skills/graphit/references/kb-explanation.md +0 -50
- package/skills/graphit/references/kb-exploration.md +0 -63
- package/skills/graphit/references/kb-graph-structure.md +0 -55
|
@@ -1,99 +1,46 @@
|
|
|
1
|
-
# KB Actions
|
|
1
|
+
# KB Actions (CLI)
|
|
2
2
|
|
|
3
|
-
Every KB
|
|
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
|
-
|
|
5
|
+
## Create
|
|
6
|
+
|
|
7
|
+
| Asset | Command |
|
|
6
8
|
|---|---|
|
|
7
|
-
|
|
|
8
|
-
|
|
|
9
|
-
|
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
|
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
|
-
##
|
|
5
|
+
## Which Command
|
|
6
6
|
|
|
7
|
-
| Question
|
|
8
|
-
|
|
9
|
-
|
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
36
|
+
### "What's left uncategorized?"
|
|
37
|
+
`graphit kb explore domain Uncategorized` - returns the tables with no home domain and their assets.
|
|
51
38
|
|
|
52
|
-
|
|
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
|
-
|
|
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
|
~~~
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Presentations
|
|
2
|
+
|
|
3
|
+
Build full-screen slide deck presentations inside Graphit canvas dashboards using `graphit.presentation()`.
|
|
4
|
+
|
|
5
|
+
## API
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
var deck = graphit.presentation('#my-deck');
|
|
9
|
+
|
|
10
|
+
deck.slide({ bg: 'dark', layout: 'center', html: '<h1>Title</h1><p>Subtitle</p>' });
|
|
11
|
+
deck.slide({ bg: 'white', layout: 'split', html: '<div>Left panel</div><div>Right panel</div>' });
|
|
12
|
+
deck.slide({ bg: 'paper', layout: 'full', html: '<div id="charts">...</div>' });
|
|
13
|
+
|
|
14
|
+
var ctrl = deck.start();
|
|
15
|
+
// ctrl.go(3) - jump to slide 4
|
|
16
|
+
// ctrl.total - number of slides
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`graphit.presentation(el)` returns a **builder**. Call `.slide()` to add slides (chainable), then `.start()` to render and wire navigation. `start()` returns a controller with `go(n)` and `total`.
|
|
20
|
+
|
|
21
|
+
## Slide Config
|
|
22
|
+
|
|
23
|
+
| Field | Type | Default | Description |
|
|
24
|
+
|-------|------|---------|-------------|
|
|
25
|
+
| `layout` | `'center'` / `'split'` / `'full'` | `'center'` | How content is arranged inside the slide |
|
|
26
|
+
| `bg` | `'paper'` / `'white'` / `'dark'` / `'teal'` / hex | `'paper'` | Fixed background color (not theme-aware) |
|
|
27
|
+
| `html` | `string` | `''` | Raw HTML content for the slide |
|
|
28
|
+
|
|
29
|
+
## Layouts
|
|
30
|
+
|
|
31
|
+
### `center` (default)
|
|
32
|
+
Content is centered horizontally and vertically. Use for title slides, quotes, big stats, CTAs.
|
|
33
|
+
|
|
34
|
+
### `split`
|
|
35
|
+
The first two child elements in `html` become left and right panels (50/50 flex). Use for text + image, comparison, side-by-side content. On mobile (<700px), panels stack vertically.
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
deck.slide({
|
|
39
|
+
layout: 'split',
|
|
40
|
+
bg: 'white',
|
|
41
|
+
html: '<div><h2>Left Title</h2><p>Text content</p></div><div><img src="data:..."></div>'
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### `full`
|
|
46
|
+
Content flows top-to-bottom with generous padding. Use for tables, grids, live data dashboards, complex layouts that need the full slide width.
|
|
47
|
+
|
|
48
|
+
## Background Themes
|
|
49
|
+
|
|
50
|
+
Backgrounds are **fixed colors** - they do not change with the app's light/dark mode. A dark slide stays dark. Content inside CAN use `var(--graphit-*)` tokens.
|
|
51
|
+
|
|
52
|
+
| Name | Background | Text Color |
|
|
53
|
+
|------|-----------|------------|
|
|
54
|
+
| `paper` | `#F7F6F2` (warm cream) | `#222224` (charcoal) |
|
|
55
|
+
| `white` | `#FFFFFF` | `#222224` |
|
|
56
|
+
| `dark` | `#222224` (charcoal) | `#F7F6F2` (cream) |
|
|
57
|
+
| `teal` | `#4DB6AC` (brand teal) | `#FFFFFF` |
|
|
58
|
+
| hex string | Custom (e.g. `#1a1a2e`) | Inherited from content HTML |
|
|
59
|
+
|
|
60
|
+
## Navigation
|
|
61
|
+
|
|
62
|
+
The slide deck includes built-in navigation:
|
|
63
|
+
- **Arrow keys**: Left/Right to navigate (guarded - skips when focus is on input/textarea)
|
|
64
|
+
- **Spacebar**: Next slide
|
|
65
|
+
- **Home/End**: First/last slide
|
|
66
|
+
- **Prev/Next buttons**: Bottom-right corner with slide counter
|
|
67
|
+
|
|
68
|
+
## Live Data Inside Slides
|
|
69
|
+
|
|
70
|
+
`graphit.resolve()` and `graphit.chart/table/kpi` work inside slides. All resolve calls fire on page load (cached queries are fast). Charts render into their target elements regardless of which slide is visible.
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
// Slide with live entities
|
|
74
|
+
deck.slide({
|
|
75
|
+
layout: 'full',
|
|
76
|
+
bg: 'white',
|
|
77
|
+
html: `
|
|
78
|
+
<h2>Live Data</h2>
|
|
79
|
+
<div data-graphit-id="spend-chart" data-graphit-label="Ad Spend"
|
|
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"
|
|
81
|
+
data-graphit-ds="ds_abc123">
|
|
82
|
+
<div id="chart1" class="gh-loading">
|
|
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>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
`
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// After deck.start(), fire resolve calls
|
|
90
|
+
deck.start();
|
|
91
|
+
|
|
92
|
+
graphit.resolve({
|
|
93
|
+
sql: "SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) AS spend FROM MARKETING_UA_DS GROUP BY 1 ORDER BY spend DESC LIMIT 6",
|
|
94
|
+
dataSourceId: "ds_abc123",
|
|
95
|
+
target: "#chart1"
|
|
96
|
+
}).then(function(r) {
|
|
97
|
+
graphit.chart("#chart1", { type: "bar", data: r.data, x: "MEDIA_SOURCE", y: "spend", valueFormat: "currency" });
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Entity Wrapping
|
|
102
|
+
|
|
103
|
+
Every data element inside slides should have full `data-graphit-*` entity wrapping, same as regular dashboards. The entity provenance panel, @ mentions, and click-to-inspect all work inside slides.
|
|
104
|
+
|
|
105
|
+
## Example: Complete Pitch Deck
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
var deck = graphit.presentation('#deck');
|
|
109
|
+
|
|
110
|
+
deck.slide({
|
|
111
|
+
bg: 'paper',
|
|
112
|
+
layout: 'center',
|
|
113
|
+
html: '<h1 style="font-size:6rem;font-weight:300">Company Update</h1><p style="font-size:1.5rem;color:#6B7280;margin-top:16px">Q2 2026</p>'
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
deck.slide({
|
|
117
|
+
bg: 'dark',
|
|
118
|
+
layout: 'split',
|
|
119
|
+
html: '<div style="color:#F7F6F2"><h2 style="font-size:3rem">The Problem</h2><p style="font-size:1.25rem;opacity:0.7;margin-top:16px">Analysts spend 80% of their time assembling dashboards instead of finding insights.</p></div><div style="display:flex;align-items:center;justify-content:center"><img src="data:image/webp;base64,..." style="max-height:60vh;border-radius:16px"></div>'
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
deck.slide({
|
|
123
|
+
bg: 'white',
|
|
124
|
+
layout: 'full',
|
|
125
|
+
html: '<h2 style="font-size:2.5rem;margin-bottom:24px">Key Metrics</h2><div style="display:grid;grid-template-columns:repeat(3,1fr);gap:16px">...</div>'
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
deck.slide({
|
|
129
|
+
bg: 'teal',
|
|
130
|
+
layout: 'center',
|
|
131
|
+
html: '<h2 style="font-size:4rem;color:#fff">Thank You</h2><p style="font-size:1.25rem;color:rgba(255,255,255,0.7);margin-top:16px">company.com</p>'
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
deck.start();
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Anti-Patterns
|
|
138
|
+
|
|
139
|
+
- **Don't put too much content on one slide.** If it scrolls, split into multiple slides.
|
|
140
|
+
- **Don't use external fonts via @import or link.** The iframe CSP blocks them. Embed as base64 @font-face or use system fonts.
|
|
141
|
+
- **Don't use external images.** Use data URIs (base64) or inline SVG.
|
|
142
|
+
- **Don't nest presentations.** One `graphit.presentation()` per dashboard.
|
|
@@ -122,12 +122,14 @@ Canvas `graphit.resolve()` queries that follow these shapes serve from a semanti
|
|
|
122
122
|
- Literal dates (`>= '2026-01-01'`), never `CURRENT_DATE` expressions
|
|
123
123
|
- GROUP BY column names or ordinals; ORDER BY / LIMIT allowed (outer only)
|
|
124
124
|
- CTEs are fine when the CTE body follows the same rules
|
|
125
|
+
- Top-N rank queries: project the sort metric in SELECT (`SELECT dim, SUM(metric) AS rv ... ORDER BY rv DESC LIMIT N`), not only in ORDER BY
|
|
125
126
|
|
|
126
127
|
**Shapes that skip the cache** (fall back to normal execution, still correct):
|
|
127
128
|
- `COUNT(DISTINCT x)`, window functions, HAVING, QUALIFY
|
|
128
129
|
- OR / NOT in WHERE
|
|
129
130
|
- Ratio metrics (`SUM(a)/NULLIF(SUM(b),0)`) - compute client-side or use two resolves
|
|
130
131
|
- `CURRENT_DATE`-relative predicates
|
|
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)
|
|
131
133
|
|
|
132
134
|
## Data Source Routing
|
|
133
135
|
|