@graphit/cli 0.1.51 → 0.1.107

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.
Files changed (61) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/.codex-plugin/plugin.json +3 -2
  4. package/README.md +99 -0
  5. package/bin/graphit +45 -0
  6. package/bin/graphit.cmd +8 -0
  7. package/bin/graphit.ps1 +38 -0
  8. package/dist/api/client.js +11 -0
  9. package/dist/api/client.js.map +1 -1
  10. package/dist/commands/auth.js +14 -6
  11. package/dist/commands/auth.js.map +1 -1
  12. package/dist/commands/ds-poll.d.ts +8 -0
  13. package/dist/commands/ds-poll.js +53 -0
  14. package/dist/commands/ds-poll.js.map +1 -0
  15. package/dist/commands/ds.js +113 -57
  16. package/dist/commands/ds.js.map +1 -1
  17. package/dist/commands/governance.js +15 -11
  18. package/dist/commands/governance.js.map +1 -1
  19. package/dist/commands/kb.js +14 -4
  20. package/dist/commands/kb.js.map +1 -1
  21. package/dist/commands/query.js +9 -7
  22. package/dist/commands/query.js.map +1 -1
  23. package/dist/index.js +2 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/output/format.d.ts +1 -0
  26. package/dist/output/format.js +6 -0
  27. package/dist/output/format.js.map +1 -1
  28. package/dist/output/table.d.ts +1 -0
  29. package/dist/output/table.js +20 -3
  30. package/dist/output/table.js.map +1 -1
  31. package/dist/skill-version.d.ts +12 -0
  32. package/dist/skill-version.js +21 -0
  33. package/dist/skill-version.js.map +1 -1
  34. package/dist/update-check.d.ts +19 -0
  35. package/dist/update-check.js +105 -12
  36. package/dist/update-check.js.map +1 -1
  37. package/hooks/hooks.json +13 -0
  38. package/package.json +11 -3
  39. package/scripts/block-legacy-setup.mjs +90 -0
  40. package/scripts/generate-commands-doc.mjs +182 -0
  41. package/scripts/plugin-status.mjs +179 -29
  42. package/scripts/sync-plugin-version.mjs +26 -4
  43. package/skills/graphit/SKILL.md +173 -433
  44. package/skills/graphit/VERSION.json +1 -1
  45. package/skills/graphit/cursor/README.md +11 -0
  46. package/skills/graphit/graphit.mdc +3 -1
  47. package/skills/graphit/references/dashboard-planning.md +1 -7
  48. package/skills/graphit/references/data-sources.md +30 -1
  49. package/skills/graphit/references/filters-advanced.md +61 -0
  50. package/skills/graphit/references/filters.md +27 -93
  51. package/skills/graphit/references/governance.md +49 -66
  52. package/skills/graphit/references/graphit-style.md +6 -18
  53. package/skills/graphit/references/kb-actions.md +44 -52
  54. package/skills/graphit/references/kb-discovery.md +10 -13
  55. package/skills/graphit/references/kb-structure.md +10 -8
  56. package/skills/graphit/references/kb-traversal.md +16 -16
  57. package/skills/graphit/references/operations.md +115 -0
  58. package/skills/graphit/references/parameterized-metrics.md +32 -44
  59. package/skills/graphit/references/runtime.md +172 -0
  60. package/skills/graphit/references/sql-reference.md +7 -40
  61. package/skills/graphit/references/domain-lenses.md +0 -116
@@ -1,84 +1,76 @@
1
- # KB Actions (CLI)
1
+ # KB Actions (Execute)
2
2
 
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.
3
+ The execute side of KB work: run approved create / update / delete through the `graphit kb` commands. The plan side - what a domain or topic is, why an asset sits where it does - lives in `kb-structure.md`. On a from-scratch build the two pair up (plan there, execute here); a normal build that reuses existing assets needs only this file.
4
+
5
+ Create only after the user approves the gap plan below. Names are stored UPPER_SNAKE_CASE. Run `graphit kb create <type> --help` for the exact flag spelling - this file teaches the recipes and policy, the CLI owns the syntax.
6
+
7
+ ## Gap Plan (present before creating anything)
8
+
9
+ When the KB-readiness gate finds the dashboard needs assets that do not exist, present a compact plan and get one approval before any write. Show, per missing asset: name, type, formula or expression, the table and topics it lands on, and any rule that applies. Then ask once.
10
+
11
+ ~~~
12
+ **KB gap - 3 assets missing for this Marketing dashboard:**
13
+
14
+ | Asset | Type | Definition | Table | Topics |
15
+ |---|---|---|---|---|
16
+ | **ROAS_D7** | metric | `SUM(revenue_d7) / NULLIF(SUM(cost), 0)` | MARKETING_UA | ATTRIBUTION |
17
+ | **CPI** | metric | `SUM(cost) / NULLIF(SUM(installs), 0)` | MARKETING_UA | ACQUISITION |
18
+ | **MEDIA_SOURCE** | dimension | `media_source` | MARKETING_UA | ACQUISITION |
19
+
20
+ Rule to apply: **EXCLUDE_ORGANIC** already exists and will filter these.
21
+ Create these so the dashboard runs on governed references? (Approve / adjust.)
22
+ ~~~
23
+
24
+ Present the plan, then stop. Do not create until the user approves.
4
25
 
5
26
  ## Create
6
27
 
7
- | Asset | Command |
28
+ | Asset | Command shape |
8
29
  |---|---|
9
- | Metric | `graphit kb create metric --name X --sql "<expr>" --table T` (optional `--topics "A,B"`, `--default-dimensions "D1,D2"`, `--skip-validate`) |
10
- | Dimension | `graphit kb create dimension --name X --expr "<expr>" --table T` (type auto-inferred; override with `--type` / `--output-type`, `--skip-validate`) |
11
- | Rule | `graphit kb create rule --name X --sql "<text>" --table T` (optional `--apply-on table:USERS metric:ARPU`, `--skip-validate`) |
30
+ | Metric | `graphit kb create metric --name X --sql "<expr>" --table T` (optional `--topics "A,B"`, `--default-dimensions "D1,D2"`, `--parameters`/`--parameters-file` for templates, `--skip-validate`) |
31
+ | Dimension | `graphit kb create dimension --name X --expr "<expr>" --table T` (type auto-inferred; override with `--type` / `--output-type`; `--skip-validate`) |
32
+ | Rule | `graphit kb create rule --name X --sql "<text>" --table T` (optional `--constraint`, `--override-policy`, `--apply-on`, `--topics`, `--skip-validate`) |
12
33
  | Synonym | `graphit kb create synonym --term X --canonical Y --type metric` |
13
34
  | Domain | `graphit kb create domain --name X` (optional `--color "#4DB6AC"`) |
14
35
  | Topic | `graphit kb create topic --name X` |
15
36
  | Relationship | `graphit kb create relationship --name X --primary-table T --primary-column C --related-table T2 --related-column C2` |
16
37
 
17
- ## Pre-Creation Validation
38
+ ## Enforceable Rule Flags
18
39
 
19
- Metric, dimension, and rule creates validate the formula against real data before writing to Firestore. On success, the response includes sample results. On failure, creation is blocked with a 422 error showing the failing query.
40
+ A plain rule is documentation. To make it enforced server-side at query time, pass typed constraints on `graphit kb create rule` / `graphit kb update rule`:
20
41
 
21
- - **Metric**: runs `SELECT {formula} FROM {table} GROUP BY {default_dims} LIMIT 5`
22
- - **Dimension**: runs `SELECT {expr} AS val, COUNT(*) AS cnt FROM {table} GROUP BY 1 ORDER BY cnt DESC LIMIT 10` (reports cardinality, NULL rate)
23
- - **Rule**: runs `SELECT COUNT(*) FROM {table} WHERE {rule}` (warns if zero rows match)
42
+ - `--constraint <spec...>` - one or more typed constraints, each written `type:value`. Types: `required_where:"<predicate>"`, `forbidden_column:<col>`, `required_filter:<col>`, `required_aggregation:<col>`, `value_restriction:<col>:<in|not_in>:<v1,v2>`. On update the supplied list REPLACES the rule's existing constraints.
43
+ - `--override-policy <policy>` - who may bypass the rule with `--override-rules`: `anyone`, `analyst_only`, `admin_only`, or `never`. Create defaults to `anyone`.
24
44
 
25
- Skip conditions (validation returns "skipped", asset still created):
26
- - Template metrics with `${PARAM:X}` placeholders
27
- - Constraint-based rules (non-empty `--constraint`)
28
- - Table has no data source and no Snowflake connections
29
- - Data source mid-refresh or unverified
45
+ What each constraint type does at query time, plus the override flow, lives in `governance.md`. Example: a rule that always scopes verified purchases -
30
46
 
31
- Use `--skip-validate` on any create command to bypass validation entirely (useful for batch creation where speed matters more than per-asset checks).
32
-
33
- ### Presenting validation results to the user
34
-
35
- The JSON response from every metric/dimension/rule create includes a `validation` object. You MUST present it to the user after each create:
36
-
37
- ```
38
- validation: {
39
- status: "pass" | "fail" | "skipped",
40
- sample_query: "SELECT ...", // the SQL that ran
41
- sample_data: [{...}, ...], // rows from real data
42
- sample_columns: ["col1", "col2"], // column names
43
- warnings: ["..."], // quality warnings
44
- skip_reason: "...", // why validation was skipped
45
- }
47
+ ```bash
48
+ graphit kb create rule --name FILTER_VERIFIED_PURCHASES --sql "Only count verified purchases" --table ORDERS --constraint required_where:"is_verified = true" --override-policy analyst_only
46
49
  ```
47
50
 
48
- How to present each status:
49
- - **pass**: render `sample_data` as a markdown table using `sample_columns` as headers. For metrics, this shows computed values. For dimensions, this shows top values with row counts. For rules, this shows the matching row count. Surface any `warnings` below the table.
50
- - **skipped**: tell the user validation was skipped and quote the `skip_reason`.
51
- - **fail** (HTTP 422, create was blocked): show the error message and `sample_query`. The asset was NOT created. Fix the formula and retry.
51
+ ## Pre-Creation Validation
52
52
 
53
- Never silently swallow validation results. The user needs to see that their formula actually works against real data.
53
+ Metric, dimension, and rule creates validate the formula against real data before writing; the response carries a `validation` object (status `pass` / `skipped` / `fail`). Surface it to the user - per-type result templates are in `kb-traversal.md`. A `fail` returns HTTP 422 and the asset is NOT created: show the error and failing SQL, fix the formula, retry. Validation is skipped (asset still created) for `${PARAM:X}` templates, constraint-based rules, and tables with no ready data source. Pass `--skip-validate` to bypass it on bulk creates.
54
54
 
55
55
  ## Update and Delete
56
56
 
57
57
  - Update a field: `graphit kb update <type> NAME --<field> value` (e.g. `--sql`, `--expr`, `--description`, `--table`).
58
- - Delete: `graphit kb delete <type> NAME --yes`.
59
-
60
- ## Topic and Reference Recipes (lists REPLACE, so don't clobber)
61
-
62
- `--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:
58
+ - Delete: `graphit kb delete <type> NAME --yes`. Deleting a parameterized parent cascades to all its children - confirm the blast radius first (see `parameterized-metrics.md`).
63
59
 
64
- - **Add a topic**: `graphit kb get metric NAME` to read current topics, then `graphit kb update metric NAME --topics "EXISTING1,EXISTING2,NEW"`.
65
- - **Remove a topic**: update with the list minus the one removed.
66
- - **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.
60
+ ## Lists REPLACE - read before you write
67
61
 
68
- Topics are horizontal - one topic can tag assets across many domains (see `kb-structure.md`).
62
+ `--topics`, `--secondary-tables`, `--default-dimensions`, and `--constraint` REPLACE the existing list, they do not append. To change one value, read the current list first (`graphit kb get metric NAME`), then write the full intended list: add a topic with `--topics "EXISTING1,EXISTING2,NEW"`; remove one by writing the list minus that value.
69
63
 
70
- ## Domain Recipes (the home is on the table and cascades)
64
+ Reference an asset onto another table with `graphit kb update metric NAME --secondary-tables "OTHER_TABLE"` (also dimension, rule). This is a read-only pointer marked `*` in the tree; metrics and dimensions need every referenced column to exist on the target, rules need only the table. Topics are horizontal - one topic can tag assets across many domains (see `kb-structure.md`).
71
65
 
72
- Domain is set on the TABLE, never per asset, and cascades to every asset on it (see `kb-structure.md` for the model):
66
+ ## Domain home (set on the table, cascades)
73
67
 
74
- - **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.
68
+ Domain is set on the TABLE, never per asset, and cascades to every asset on it (model in `kb-structure.md`). To re-home a whole table at once: `graphit kb update table NAME --domain MARKETING`. Change it once on the table, never asset by asset.
75
69
 
76
70
  ## Navigation
77
71
 
78
- 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.
72
+ To find what exists and how it connects, use the read recipes in `kb-traversal.md`.
79
73
 
80
74
  ## UI-Only (no CLI command)
81
75
 
82
- These are platform-UI state or platform-only, not KB data the CLI changes:
83
- - View mode (Tree / By Topic / By Table / Flat), filter dropdowns, expand / collapse, drag-drop onto a topic.
84
- - Cross-cutting synonym domains (extra relevance beyond the home domain) are set in the platform UI - there is no CLI flag for them yet.
76
+ Platform-UI state, not KB data the CLI changes: view mode (Tree / By Topic / By Table / Flat), filter dropdowns, expand / collapse, drag-drop onto a topic, and a synonym's cross-cutting domains (extra relevance beyond its home domain has no CLI flag yet).
@@ -4,15 +4,15 @@ Consult when starting a dashboard build, or when the user references a business
4
4
 
5
5
  ## Domain-First Discovery
6
6
 
7
- Work narrow, not broad: **domain -> data source -> assets.** Listing or searching everything is the fallback, not the default.
7
+ Work narrow, not broad: **domain -> data source -> assets.** The first move is always to explore one domain, not to list or search the whole KB.
8
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.
9
+ 1. **Scope to the domain.** Ask the user which business area this is about - present the real domains and let them pick; do not assume the domain. When a concept could span domains and you cannot name the domain, `graphit kb explore topic <NAME>` returns everything tagged with that concept across every domain - use it to land on the right domain.
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, and it is the primary discovery call.
11
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
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.
13
+ 5. **Broaden only as a fallback.** Use `graphit kb search "<concept>"` only when an exploration came up empty or the concept name is too fuzzy to match a domain or topic. Search is semantic (matches by meaning, ranked by a relevance `score`) merged with name/text matching, and capped by `--limit` - the result carries `total` and `truncated`. So an empty or unexpected result means *maybe ranked-out or truncated*, never proof an asset is absent: raise `--limit`, narrow `--type`, or confirm a specific name with `kb get` before concluding it doesn't exist. (`graphit kb list domains` enumerates domain names when you need to pick one; it is a name lookup, not the asset-discovery path - explore the domain to see its assets.)
14
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.
15
+ If a domain has tables but no metrics or dimensions, that is the strongest signal to propose foundational assets before building any graph - this is the KB-readiness gate the build workflow enforces. If the user declines ("just build it", "skip KB"), respect it and work from the table schema.
16
16
 
17
17
  ## Metric vs Dimension
18
18
 
@@ -33,7 +33,7 @@ If a domain has tables but no metrics or dimensions, that is the strongest signa
33
33
  | User describes a business rule ("active = logged in within 30 days") | Rule | Applied automatically to future queries |
34
34
  | User uses a business term not in KB ("GMV", "churn", "ARPU") | Synonym | Maps colloquial terms to a defined metric |
35
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?"
36
+ Propose creation as the default path, in plain language: "Your KB has no revenue metric yet. I'd create one first - then it's reusable across every dashboard with a consistent formula, and I'll build the graph on it. Sound good?"
37
37
 
38
38
  ## Naming Conventions
39
39
 
@@ -66,15 +66,12 @@ CASE WHEN USERS.AGE >= 18 THEN 'adult' ELSE 'minor' END
66
66
 
67
67
  Use CASE WHEN for conditionals (Snowflake has no FILTER WHERE). Always guard division with NULLIF.
68
68
 
69
- 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`.
69
+ When creating assets, pass `--topics` to tag the business concept (check existing topic names first) and `--default-dimensions` on a metric to declare its natural grouping axes. Dimension types are auto-inferred from the column schema; override with `--type` / `--output-type` only when the inference is wrong. Full create / update / delete matrix: `kb-actions.md`.
70
70
 
71
71
  ## Reuse Over Reinvention
72
72
 
73
- When a dashboard has several graphs sharing a concept, propose ONE KB asset instead of repeating the formula:
73
+ When several graphs share a concept, propose ONE KB asset instead of repeating the formula: 3 graphs using `SUM(ORDERS.AMOUNT)` become one `TOTAL_REVENUE` metric; 2 graphs grouping by `DATE_TRUNC('month', TS)` become one `MONTHLY` dimension.
74
74
 
75
- - 3 graphs all using `SUM(ORDERS.AMOUNT)` -> one `TOTAL_REVENUE` metric.
76
- - 2 graphs grouping by `DATE_TRUNC('month', TS)` -> one `MONTHLY` dimension.
75
+ 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 a metric available on another table.
77
76
 
78
- 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".
79
-
80
- 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`.
77
+ For the graph model and structural questions (vertical home vs horizontal topics), see `kb-structure.md`; for read commands and result templates, see `kb-traversal.md`.
@@ -1,6 +1,8 @@
1
- # KB Structure
1
+ # KB Structure (Plan)
2
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).
3
+ This is the plan side of KB work: how the Knowledge Base is organized, so you can explain it and design what to build. The execute side - the actual `graphit kb` create / update / delete commands - lives in `kb-actions.md`. On a from-scratch KB build the two pair up: design the graph here, then run the commands there. On a normal build that reuses existing assets, you mostly read this to answer a structural question.
4
+
5
+ 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
6
 
5
7
  ## Node Types
6
8
 
@@ -30,13 +32,13 @@ How the Knowledge Base is organized, and how to answer structural questions abou
30
32
 
31
33
  Two different axes organize the KB - keep them separate:
32
34
 
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
+ - **Vertical: the home (domain -> data source -> assets).** Containment that cascades. An asset has ONE home domain, inherited from its primary table (the data source its SQL reads). Set the domain on the **table** (`graphit kb update table NAME --domain MARKETING`) and every asset on it 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.
36
+ - **Horizontal: the concept (topics).** Topics cut ACROSS domains. The same topic (e.g. RETENTION) can tag assets in different domains, grouping them by business meaning regardless of where they sit. 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 by-topic view is how you find everything about it.
35
37
 
36
38
  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.
39
+ - **Multiple tables**: a metric or dimension can depend on several tables (a JOIN across ORDERS and CUSTOMERS) and appears under each. Table dependency is structural, derived from the SQL, never tagged manually.
40
+ - **Referencing (`secondary_tables`)**: an asset can be referenced onto extra tables; it shows under the target with a `*` suffix, links back to the original, and stays editable only from its home table. Table-backed assets derive domain membership from all their tables (primary plus `secondary_tables`).
41
+ - **Cross-cutting domains**: synonyms can carry extra domains in `extra_domain_ids` for relevance beyond their home, without moving the asset in the tree.
40
42
 
41
43
  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
44
 
@@ -61,4 +63,4 @@ Verify against actual KB state when the question names a specific asset (`graphi
61
63
 
62
64
  ## Semantic Questions (Tier 2 - Different Handling)
63
65
 
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.
66
+ Questions about what a concept MEANS at this org ("what does revenue mean for us?", "how do we define an active user?") are semantic, not structural. 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,47 +1,47 @@
1
1
  # KB Traversal - Worked Examples
2
2
 
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).
3
+ Contents: Which Command (the read-command picker) - Common Queries (worked examples) - Reading the Results - Presenting KB Results (the per-command output templates).
4
+
5
+ Which `graphit kb` read command answers each question, and how to present the result. Explore is the primary call: `graphit kb explore <type> NAME` walks the graph around one entity and returns its whole neighborhood at once. `graphit kb get <type> NAME` returns one asset's full details, and `graphit kb search "<query>"` is the fallback for semantic discovery when you cannot name the entity.
4
6
 
5
7
  ## Which Command
6
8
 
7
9
  | Question | Command |
8
10
  |---|---|
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 is inside a domain or topic (the build entry point) | `graphit kb explore domain NAME` / `graphit kb explore topic NAME` |
11
12
  | 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
+ | Get one asset's full details | `graphit kb get metric NAME` |
14
+ | Find things like X when you cannot name it (semantic) | `graphit kb search "X"` (add `--type metric` to narrow) |
13
15
 
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.
16
+ `graphit kb explore` returns the whole neighborhood in one call and has no edge-type or depth flags - the response already carries dependents, joins, topics, and domain together, so read the part you need instead of chaining calls.
15
17
 
16
18
  ## Common Queries
17
19
 
20
+ ### "Show me everything in the MARKETING domain"
21
+ `graphit kb explore domain MARKETING` - returns the domain's tables and the assets on them. This is the first call when scoping a build.
22
+
18
23
  ### "Find all revenue metrics"
19
- `graphit kb search "revenue" --type metric` for semantic matches. To get the assets tagged with a specific topic, `graphit kb explore topic REVENUE`.
24
+ `graphit kb explore topic REVENUE` returns the assets tagged with that concept across every domain. Fall back to `graphit kb search "revenue" --type metric` only when no topic captures the concept.
20
25
 
21
26
  ### "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.
27
+ `graphit kb explore table ORDERS` - lists every metric and dimension whose SQL references ORDERS columns, plus the relationships it joins through.
23
28
 
24
29
  ### "What joins with MARKETING_UA_DS?"
25
30
  `graphit kb explore table MARKETING_UA_DS`, then read the relationships in the response (the documented JOINs involving that table).
26
31
 
27
- ### "What domains do we have?"
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.
29
-
30
- ### "Show me everything in the MARKETING domain"
31
- `graphit kb explore domain MARKETING` - returns the domain's tables and the assets on them.
32
-
33
32
  ### "What topics does ARPU_D1 belong to?"
34
33
  `graphit kb explore metric ARPU_D1` - the response includes its topics, along with its tables, dimensions, and home domain.
35
34
 
36
35
  ### "What's left uncategorized?"
37
36
  `graphit kb explore domain Uncategorized` - returns the tables with no home domain and their assets.
38
37
 
39
- ## Reading the Results
40
-
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.
38
+ ### "What domains do we have?" (enumerate names)
39
+ `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. This enumerates domain NAMES; to see what is in one, explore it. Domain is not a searchable type, so `graphit kb search` will not surface domains.
42
40
 
43
41
  ## Presenting KB Results
44
42
 
43
+ A single `graphit kb explore` call returns one entity's full neighborhood, so it usually answers the question on its own - scan the response and run a second command only if the first does not contain what you need. The user cannot see raw CLI output; render every result with these per-command templates.
44
+
45
45
  **After `graphit kb list <type>`** - count summary + table with bold names:
46
46
 
47
47
  ~~~
@@ -0,0 +1,115 @@
1
+ # CLI Operations and Health
2
+
3
+ Load this when the concern is the Graphit CLI or plugin itself, not the analysis: the session-start banner, a health check, installing or updating, a permission error (403/404/423), or reporting a failure or partial result. Skip it on every healthy build or query turn.
4
+
5
+ ## Contents
6
+
7
+ - [Session start banner](#session-start-banner)
8
+ - [Health gate](#health-gate)
9
+ - [Install and update](#install-and-update)
10
+ - [Legacy copied-file setup](#legacy-copied-file-setup)
11
+ - [Permission errors](#permission-errors)
12
+ - [Output contract](#output-contract)
13
+ - [Reporting failures and partial results](#reporting-failures-and-partial-results)
14
+
15
+ Governance is enforced server-side by the query gateway. There is no client-side query guard, so never claim to block a query locally; a query is rejected by the platform, not the CLI.
16
+
17
+ ## Session start banner
18
+
19
+ Before anything else, run `graphit plugin status` and show a version banner:
20
+
21
+ ```
22
+ Graphit
23
+ Skill: {skill_version from the router frontmatter}
24
+ CLI: {output of graphit --version}
25
+ Status: {OK, or the action the plugin status reports}
26
+ ```
27
+
28
+ If the status reports action needed, show the exact remediation it prints before proceeding. If it fails with "command not found" or "unknown command", the CLI is too old: show `CLI: {version} (outdated)` and tell the user to update with `npm install -g @graphit/cli@latest`. Do not proceed with an outdated CLI, because commands and flags in this skill may not exist in old versions.
29
+
30
+ ## Health gate
31
+
32
+ ALWAYS run `graphit plugin status` before diagnosing, retrying, or inventing a workaround when CLI behavior looks wrong: unknown commands, unrecognized flags, missing commands this skill describes, stale-looking output, copied-skill warnings, non-zero exits with unclear messages, or the user saying Graphit, the CLI, the plugin, or the skill is not working.
33
+
34
+ If the status reports action needed, stop and tell the user the exact remediation first. Do not continue with stale instructions unless the user explicitly asks you to proceed anyway.
35
+
36
+ Do NOT suggest updating for normal operational failures: expired auth, bad SQL, a network timeout, or an entity not found. Those are runtime issues, not version drift.
37
+
38
+ ## Install and update
39
+
40
+ Two separate artifacts ship to Claude Code and Codex: the **CLI binary** (`@graphit/cli`, installed and updated with npm) and the **skill bundle** (the plugin `graphit@graphit-plugin`, updated through the assistant's plugin manager). The plugin bundle does NOT contain the CLI binary, so updating one never updates the other.
41
+
42
+ `graphit plugin status` reports "update available" for the **npm CLI binary**. Update it with `npm install -g @graphit/cli@latest` (not `npm update -g`, which can keep you on an old release). If `graphit` resolves to a custom npm prefix - compare `command -v graphit` with `npm prefix -g` - reinstall to that prefix: `npm install -g @graphit/cli@latest --prefix <dir>`, where `<dir>` is the parent of the bin directory holding graphit. The **skill bundle** updates separately with `claude plugin update graphit@graphit-plugin`; that never touches the binary.
43
+
44
+ How you run the binary depends on the surface: on Claude Code the plugin's `graphit` wrapper runs it directly; on Codex, Cursor, a terminal, or CI, invoke it explicitly with `npx -y @graphit/cli@<version>` (or pin `npx -y @graphit/cli@<exact>` for a deterministic, reproducible run).
45
+
46
+ `graphit setup` is only for legacy or fallback copied-file installs, mainly Cursor or environments without plugin support. If `graphit plugin status` reports Claude Code or Codex copied snapshots, tell the user to remove them with `graphit setup --remove-legacy-copies` after confirming the plugin is installed. Use `graphit setup --legacy-copy` only when the plugin is unavailable.
47
+
48
+ For the exact flags, run the command with `--help` (for example `graphit setup --help`). Do not hand-author flag lists; the CLI is the source of truth.
49
+
50
+ ## Legacy copied-file setup
51
+
52
+ After an intentional legacy copied-file setup completes, offer to add a Graphit section to the project instructions so future sessions know Graphit is available. Do not suggest legacy setup for Claude Code or Codex when the plugin is available. Suggested snippet:
53
+
54
+ ```
55
+ ## Graphit
56
+ Use the Graphit skill to build custom HTML dashboards from real data.
57
+ Run `graphit ds list` to see cached data sources before querying.
58
+ Run `graphit kb list metric` to explore available metrics and dimensions.
59
+ ```
60
+
61
+ ## Permission errors
62
+
63
+ The CLI enforces the same permission model as the platform. Three codes:
64
+
65
+ | Code | Meaning | What to tell the user |
66
+ |---|---|---|
67
+ | 403 | Analyst seat or admin role required | Most commands need an Analyst seat. Viewer-seat users can run `graphit auth` only; everything else is blocked, so they use the platform UI. Connector create and delete also need an org owner or admin role, so a non-admin analyst gets 403 there. |
68
+ | 404 | Not found, or no access | Returned for dashboards the user cannot reach (private ones owned by others, team dashboards they are not on). The API does not distinguish "does not exist" from "you cannot access it", to prevent ID enumeration. Do not assume the dashboard is gone. |
69
+ | 423 | Shared dashboard needs an active editing session | Shared-dashboard mutations need the user to open the dashboard on the platform and click Edit first; the CLI cannot acquire the session. See the shared-dashboard constraint in the router. |
70
+
71
+ For the exact remediation flags on the failed command, run it with `--help`.
72
+
73
+ ## Output contract
74
+
75
+ Commands write only the result payload to stdout; all decoration (progress, tables, provenance, warnings, summaries, links) goes to stderr.
76
+
77
+ - `--output json` (default): stdout is exactly one valid JSON document - parse it directly, never strip a trailing line. Provenance and warnings live inside the JSON.
78
+ - Use json for anything you read back, especially nested output (`kb explore`, query rows, `kb get`). `--output table` is a quick flat human view; nested cells render as compact JSON, not `[object Object]`.
79
+ - `--output styled` is the interactive view - never parse it.
80
+
81
+ ## Reporting failures and partial results
82
+
83
+ When a command fails or only part of a multi-step task succeeds, report it truthfully. The user cannot see the raw CLI output, so a bare "something went wrong" leaves them stuck. Verify each step before claiming it; never report a step as done that you did not confirm.
84
+
85
+ State three things: what succeeded, what failed (with the cause from the CLI output), and the single next step. Distinguish a transient failure (a network timeout, a mid-refresh data source - worth one retry) from a non-transient one (bad SQL, an entity not found, a permission code, a governance rejection - needs a fix, not a retry).
86
+
87
+ Failure template:
88
+
89
+ ~~~
90
+ **Failed:** {what you tried, in plain terms}.
91
+
92
+ **Cause:** {the concrete reason from the CLI output - the error code, the rejected rule, the SQL error}.
93
+
94
+ **Next:** {the one action to take - fix the formula, enter Edit mode, run the named command, or ask the user a specific question}.
95
+ ~~~
96
+
97
+ Partial-success template (some steps landed, some did not):
98
+
99
+ ~~~
100
+ **Done:** {the steps that succeeded, named}.
101
+
102
+ **Not done:** {the steps that failed, each with its cause}.
103
+
104
+ **Next:** {the single next step to finish or recover}.
105
+ ~~~
106
+
107
+ Worked example of a partial KB creation:
108
+
109
+ ~~~
110
+ **Done:** Created **CPI** and **ROAS_D7** on **MARKETING_UA**; both validated on real data.
111
+
112
+ **Not done:** **LTV_CAC_RATIO** was blocked (422) - its formula references **LTV**, which does not exist yet.
113
+
114
+ **Next:** Create **LTV** first, then retry **LTV_CAC_RATIO**. Want me to define **LTV** now?
115
+ ~~~
@@ -1,27 +1,17 @@
1
1
  # Parameterized Metrics
2
2
 
3
- A parameterized metric is a template with `${PARAM:NAME}` tokens in its calculation. Each token maps to a named parameter with a list of values (value_name to SQL fragment). The system auto-generates child variants for every combination of parameter values.
4
-
5
- Example: ROAS with parameters REVENUE (9 values) and DN (4 values) produces 36 validated child metrics from 1 template.
3
+ A parameterized metric is a template whose calculation contains `${PARAM:NAME}` tokens (dollar sign and curly braces). Each token maps to a named parameter with a list of values (a value name paired with the SQL fragment it substitutes in). The system auto-generates one validated child variant per combination of values - a ROAS template with REVENUE (9 values) and DN (4 values) produces 36 children.
6
4
 
7
5
  ## When to Parameterize
8
6
 
9
- **Create parameterized** when columns follow a variant pattern:
10
- - `total_iap`, `total_iap_new_users`, `estimated_gross_iap` -> one REVENUE parameter
11
- - Metrics that differ only by a WHERE clause (D7, D30, D90) -> one DN parameter
12
-
13
- **Stay standalone** when:
14
- - Metric has no natural variants
15
- - Only 1-2 variants (template overhead not worth it)
16
- - Variants use fundamentally different formulas
7
+ Parameterize when columns or formulas follow a variant pattern: `total_iap` / `total_iap_new_users` / `estimated_gross_iap` map to one REVENUE parameter; metrics that differ only by a WHERE window (D7, D30, D90) map to one DN parameter. Stay standalone when the metric has no natural variants, has only one or two (template overhead is not worth it), or when variants use fundamentally different formulas.
17
8
 
18
- ## Creating a Parameterized Metric
9
+ ## Authoring a Template
19
10
 
20
- Use `${PARAM:NAME}` tokens in the calculation, then pass `parameters` with the value map. The `placeholder` field is optional (omit it for the new token format):
11
+ Put `${PARAM:NAME}` tokens in the calculation, then pass the value map with `--parameters` (inline JSON array) or `--parameters-file <path>` on `graphit kb create metric` / `graphit kb update metric`. Run `graphit kb create metric --help` for the exact flag spelling.
21
12
 
22
13
  ```json
23
- "calculation": "SUM(${PARAM:REVENUE}) / SUM(cost) * 100 ${PARAM:DN}",
24
- "parameters": [
14
+ [
25
15
  {
26
16
  "name": "REVENUE",
27
17
  "values": {"ALL_BOOKINGS": "total_iap", "NEW_BOOKINGS": "total_iap_new_users"},
@@ -35,55 +25,53 @@ Use `${PARAM:NAME}` tokens in the calculation, then pass `parameters` with the v
35
25
  ]
36
26
  ```
37
27
 
28
+ Paired with a calculation such as `SUM(${PARAM:REVENUE}) / SUM(cost) * 100 ${PARAM:DN}`.
29
+
38
30
  | Rule | Detail |
39
31
  |------|--------|
40
- | Token matching | Every `${PARAM:NAME}` in the calculation must have a matching parameter by name |
32
+ | Token match | Every `${PARAM:NAME}` token in the calculation needs a matching parameter by name |
41
33
  | Empty values | Valid as no-ops (e.g. D0 adds no filter) |
42
- | Variant cap | Max 100 (Cartesian product of all parameter values) |
43
- | Child naming | Auto-generated: `{PARENT}_{VALUE1}_{VALUE2}` (e.g. ROAS_ALL_BOOKINGS_D7) |
34
+ | Variant cap | Max 100 (the Cartesian product of all parameter values) |
35
+ | Child naming | Auto-generated as `{PARENT}_{VALUE1}_{VALUE2}` (e.g. ROAS_ALL_BOOKINGS_D7) |
36
+ | Validation | Template create skips per-formula validation (children validate on generation) |
44
37
 
45
38
  ## Editing a Template
46
39
 
47
- All edits go through the parent template. Children are read-only.
40
+ All edits go through the parent template; children are read-only. Run a fresh `--parameters` array to change the value sets.
48
41
 
49
42
  | User intent | Edit action |
50
43
  |-------------|-------------|
51
- | Change formula | Edit `calculation` on the template - children re-generate |
52
- | Add a variant (e.g. D120) | Edit `parameters`, add the new value to the relevant parameter |
53
- | Remove variants | Edit `parameters`, remove values from the relevant parameter |
54
- | Change description/topics | Edit those fields on the template - no child re-generation |
44
+ | Change formula | Update `--sql` on the template - children re-generate |
45
+ | Add a variant (e.g. D120) | Update `--parameters` with the new value added to the relevant parameter |
46
+ | Remove variants | Update `--parameters` with values removed from the relevant parameter |
47
+ | Change description or topics | Update those fields on the template - no child re-generation |
55
48
 
56
- If a child metric is targeted for edit or delete, redirect to the parent:
57
- "ROAS_ALL_BOOKINGS_D7 is a variant of ROAS. Edit the parent template ROAS instead."
49
+ If the user targets a child for edit or delete, redirect to the parent: "ROAS_ALL_BOOKINGS_D7 is a variant of ROAS. Edit the parent template ROAS instead."
58
50
 
59
- ## Using Parameterized Metrics in Queries
51
+ ## Using a Variant in a Query
60
52
 
61
- When a user asks for a specific variant (e.g. "show me D7 ROAS for new users"):
53
+ You never substitute tokens by hand. The server resolves a variant when you reference it with the parameter syntax in SQL.
62
54
 
63
- 1. Find the template in KB context (marked with `[P:N]` in the tree)
64
- 2. Infer axis values from the user's message: REVENUE=NEW_BOOKINGS, DN=D7
65
- 3. Use `read` with `entity_type="metric"` and pass `axis_values` to get the resolved child
66
- 4. Use the returned validated child calculation in your graph SQL
55
+ 1. Find the template. `graphit kb list metric` shows a `params` column listing each metric's required parameter names (e.g. `REVENUE, DN`); an empty `params` cell means the metric is flat and needs no parameters.
56
+ 2. Map the user's words to parameter values: "D7 ROAS for new users" maps to `REVENUE=NEW_BOOKINGS` and `DN=D7`.
57
+ 3. Reference it with `{{metric:NAME(K=V)}}` in the query SQL, e.g. `{{metric:ROAS(REVENUE=NEW_BOOKINGS, DN=D7)}}`. The server expands it to the pre-validated child calculation at query time.
67
58
 
68
- ### NEVER
69
- - Substitute tokens manually in the template formula. Always use the backend-returned child SQL, which has been pre-validated.
59
+ ```bash
60
+ graphit query "SELECT {{dim:INSTALL_MONTH}}, {{metric:ROAS(REVENUE=NEW_BOOKINGS, DN=D7)}} AS roas FROM MARKETING_UA_DS GROUP BY 1" --ds ds_abc123 --verbose
61
+ ```
70
62
 
71
- ### Defaults and Errors
72
- - **Default resolution:** When the user doesn't specify parameters, pass empty `axis_values` - the backend fills defaults.
73
- - **Invalid values:** If you pass an invalid axis value, the backend returns valid options. Retry with a valid value or ask the user.
63
+ Defaults and errors:
64
+ - Omitting a parameter on a required metric returns a clear error naming the parameter and the valid values. Retry with a value or ask the user which one they meant.
65
+ - A pre-baked variant (ROAS_D7, ARPU_D30) has its value hardcoded in the name, so reference it plainly as `{{metric:ROAS_D7}}` with no parameter clause.
74
66
 
75
67
  ## Contrast: Wrong vs Right
76
68
 
77
69
  | Wrong | Right |
78
70
  |-------|-------|
79
- | Manually replace `${PARAM:REVENUE}` with `total_iap` in the formula and pass to graph SQL | Use `read` with `axis_values` to get pre-validated child calculation |
80
- | Create separate ROAS_ALL_BOOKINGS, ROAS_NEW_BOOKINGS as standalone metrics | Create one ROAS template with a REVENUE parameter |
71
+ | Replace `${PARAM:REVENUE}` with `total_iap` by hand and put that in the query SQL | Reference `{{metric:ROAS(REVENUE=ALL_BOOKINGS, DN=D7)}}` and let the server expand the validated child |
72
+ | Create ROAS_ALL_BOOKINGS and ROAS_NEW_BOOKINGS as separate standalone metrics | Create one ROAS template with a REVENUE parameter |
81
73
  | Edit ROAS_ALL_BOOKINGS_D7 directly | Edit the parent ROAS template instead |
82
74
 
83
- ## Deleting a Template
84
-
85
- Delete on the parent cascade-deletes all children. Blast radius includes all children plus their downstream refs (graphs, rules, synonyms).
86
-
87
- ## Verification
75
+ ## Delete and Verify
88
76
 
89
- Verify on a template sets `verified=True` on the parent and all children.
77
+ Deleting the parent cascade-deletes every child; the blast radius includes all children plus their downstream references (graphs, rules, synonyms), so confirm before deleting. Verifying the parent sets `verified=true` on the parent and every child at once.