@graphit/cli 0.2.322 → 0.2.323

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 (62) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/bin/graphit +1 -1
  5. package/bin/graphit.ps1 +1 -1
  6. package/dist/api/client.js +15 -0
  7. package/dist/api/client.js.map +1 -1
  8. package/dist/commands/ds-config.js +2 -2
  9. package/dist/commands/ds-config.js.map +1 -1
  10. package/dist/commands/ds.js +2 -2
  11. package/dist/commands/ds.js.map +1 -1
  12. package/dist/commands/kb.js +347 -15
  13. package/dist/commands/kb.js.map +1 -1
  14. package/dist/commands/query.js +3 -3
  15. package/dist/commands/query.js.map +1 -1
  16. package/dist/index.js +0 -8
  17. package/dist/index.js.map +1 -1
  18. package/dist/skill-guard.js +2 -1
  19. package/dist/skill-guard.js.map +1 -1
  20. package/package.json +1 -1
  21. package/scripts/verb-policy-source.json +30 -102
  22. package/skills/graphit/SKILL.md +31 -39
  23. package/skills/graphit/VERSION.json +1 -1
  24. package/skills/graphit/references/data-source-refresh.md +27 -0
  25. package/skills/graphit/references/data-sources.md +27 -122
  26. package/skills/graphit/references/filters-advanced.md +2 -2
  27. package/skills/graphit/references/governance-explained.md +18 -29
  28. package/skills/graphit/references/governance.md +20 -83
  29. package/skills/graphit/references/kb-actions.md +28 -81
  30. package/skills/graphit/references/kb-discovery.md +35 -64
  31. package/skills/graphit/references/kb-scope.md +17 -15
  32. package/skills/graphit/references/kb-structure.md +28 -54
  33. package/skills/graphit/references/kb-traversal.md +24 -96
  34. package/skills/graphit/references/metric-families.md +19 -0
  35. package/skills/graphit/references/migration.md +2 -2
  36. package/skills/graphit/references/onboarding.md +5 -4
  37. package/skills/graphit/references/presentations.md +1 -1
  38. package/skills/graphit/references/runtime.md +6 -6
  39. package/skills/graphit/references/semantic-authoring.md +65 -0
  40. package/skills/graphit/references/sql-reference.md +10 -20
  41. package/dist/commands/kb-constraints.d.ts +0 -14
  42. package/dist/commands/kb-constraints.js +0 -53
  43. package/dist/commands/kb-constraints.js.map +0 -1
  44. package/dist/commands/kb-create.d.ts +0 -2
  45. package/dist/commands/kb-create.js +0 -296
  46. package/dist/commands/kb-create.js.map +0 -1
  47. package/dist/commands/kb-delete.d.ts +0 -2
  48. package/dist/commands/kb-delete.js +0 -37
  49. package/dist/commands/kb-delete.js.map +0 -1
  50. package/dist/commands/kb-read.d.ts +0 -2
  51. package/dist/commands/kb-read.js +0 -223
  52. package/dist/commands/kb-read.js.map +0 -1
  53. package/dist/commands/kb-shared.d.ts +0 -43
  54. package/dist/commands/kb-shared.js +0 -81
  55. package/dist/commands/kb-shared.js.map +0 -1
  56. package/dist/commands/kb-update.d.ts +0 -2
  57. package/dist/commands/kb-update.js +0 -240
  58. package/dist/commands/kb-update.js.map +0 -1
  59. package/dist/commands/sl/index.d.ts +0 -2
  60. package/dist/commands/sl/index.js +0 -263
  61. package/dist/commands/sl/index.js.map +0 -1
  62. package/skills/graphit/references/parameterized-metrics.md +0 -77
@@ -1,97 +1,44 @@
1
- # KB Actions (Execute)
1
+ # KB Actions
2
2
 
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`.
3
+ Load when an approved gap must be authored or an existing semantic asset changed.
4
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.
5
+ ## Approval gate
6
6
 
7
- ## Gap Plan (present before creating anything)
7
+ Before writing, present the missing concept, proposed root, exact definition, group/access scope, and verification state. Do not write until the user approves.
8
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.
9
+ ## Authoring contract
10
10
 
11
- ~~~
12
- **KB gap - 3 assets missing for this Marketing dashboard:**
11
+ - Create semantic models, metrics, groups, and retained rules from JSON.
12
+ - Use exactly one of `--file` or `--json`.
13
+ - Names are lowercase snake case; reserve `__` for qualified dimensions.
14
+ - Entities, dimensions, and measures mutate only through semantic-model update.
15
+ - A supplied nested list replaces the stored list whole. Read first and include every sibling that must remain.
16
+ - Explicit `meta` replaces author metadata whole. Preserve family, axes, topics, and other author fields.
17
+ - Use dedicated verify/unverify actions. Never patch metadata merely to change verification.
13
18
 
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
+ Read `semantic-authoring.md` for model/metric shapes and `metric-families.md` for concrete variants.
19
20
 
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
- ~~~
21
+ ## Rules
23
22
 
24
- Present the plan, then stop. Do not create until the user approves.
23
+ Rules remain Graphit objects. Create them from JSON with body/constraints plus `apply_on` targets. Final targets are model, entity, dimension, metric, or group identities. A rule without targets is refused.
25
24
 
26
- ## Create
25
+ Constraints keep their five semantics: required predicate, forbidden column, required filter, required aggregation, and value restriction. Use declared semantic identities and typed values.
27
26
 
28
- | Asset | Command shape |
29
- |---|---|
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`, `--apply-on`, `--topics`, `--skip-validate`) |
33
- | Synonym | `graphit kb create synonym --term X --canonical Y --type metric` |
34
- | Domain | `graphit kb create domain --name X` (optional `--color "#4DB6AC"`) |
35
- | Topic | `graphit kb create topic --name X` |
36
- | Relationship | `graphit kb create relationship --name X --primary-table T --primary-column C --related-table T2 --related-column C2` |
27
+ ## Update
37
28
 
38
- ## Enforceable Rule Flags
29
+ 1. Read the target through the current principal.
30
+ 2. Preserve complete nested and metadata structures.
31
+ 3. Apply the smallest patch.
32
+ 4. Re-read immediately.
33
+ 5. Verify/unverify separately when intended.
34
+ 6. Inspect receipts; a degraded write may have landed and must not be retried blindly.
39
35
 
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`:
36
+ ## Delete
41
37
 
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.
38
+ Confirm with the user and inspect usage first. The server checks known definition dependencies, not every canvas reference. A green guard is not exhaustive impact proof.
43
39
 
44
- What each constraint type does at query time, plus the override flow, lives in `governance.md`. Example: a rule that always scopes verified purchases -
40
+ ## Permissions
45
41
 
46
- ```bash
47
- graphit kb create rule --name FILTER_VERIFIED_PURCHASES --sql "Only count verified purchases" --table ORDERS --constraint required_where:"is_verified = true"
48
- ```
42
+ Read access is the ceiling for writes. `kb_write` comes from the effective policy key. Group lifecycle is admin-only. Hidden and missing targets return the same absence.
49
43
 
50
- ## Rule Targeting - what a rule governs
51
-
52
- Every rule must apply to at least one asset; a targetless rule is rejected. `--table` is required, and without `--apply-on` the rule governs that **whole table** - which cascades to every metric and dimension on it (it fires on any query touching the table). To narrow instead, pass `--apply-on metric:NAME` / `--apply-on dimension:NAME` so the rule applies only when that asset is used. The two are mutually exclusive: never mix a table target with metric/dimension targets on one rule. To govern several tables, list each: `--apply-on table:A table:B`.
53
-
54
- ```bash
55
- # Whole table - cascades to every metric/dimension on ORDERS
56
- graphit kb create rule --name ORDERS_ACTIVE_ONLY --sql "Exclude cancelled orders" --table ORDERS
57
- # Narrowed - applies only when the ARPU metric is used
58
- graphit kb create rule --name ARPU_TRIM --sql "Cap ARPU outliers" --table USERS --apply-on metric:ARPU
59
- ```
60
-
61
- ## Pre-Creation Validation
62
-
63
- 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.
64
-
65
- ## Update and Delete
66
-
67
- - Update a field: `graphit kb update <type> NAME --<field> value` (e.g. `--sql`, `--expr`, `--description`, `--table`).
68
- - 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`).
69
-
70
- ## Lists REPLACE - read before you write
71
-
72
- `--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.
73
-
74
- Reference a **metric or dimension** onto another table with `graphit kb update metric NAME --secondary-tables "OTHER_TABLE"` (also dimension). This is a read-only pointer marked `*` in the tree; every referenced column must exist on the target. For **rules**, govern several tables by listing them in `--apply-on` (`--apply-on table:A table:B`), not `--secondary-tables`. Topics are horizontal - one topic can tag assets across many domains (see `kb-structure.md`).
75
-
76
- ## Domain home (set on the table, cascades)
77
-
78
- 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.
79
-
80
- ## Who can write what
81
-
82
- Reads are open to every member; writes are scoped by the caller's data access profile. Check `graphit status` before presenting a gap plan, so the plan is one they can execute.
83
-
84
- | Write | Needs |
85
- |---|---|
86
- | Metric, dimension, rule, synonym, relationship, table | `kb_write` in the asset's domain |
87
- | Moving an asset or table to another domain | `kb_write` in BOTH domains - the one it leaves and the one it enters |
88
- | Domain and topic create / update / delete | Org admin; a profile never grants it |
89
- | Template create / update / delete | Org admin, OR `kb_write` in any one domain - never a per-template or per-domain grant |
90
-
91
- Every member can read and use templates. Status is advisory; a denial with `retryable: false` is a stop, not a retry (`operations.md`).
92
-
93
- To find what exists and how it connects, use the read recipes in `kb-traversal.md`.
94
-
95
- ## UI-Only (no CLI command)
96
-
97
- 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).
44
+ Group `access` is stored dbt metadata; it does not grant Graphit visibility.
@@ -1,79 +1,50 @@
1
1
  # KB Discovery
2
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.
3
+ Load before querying, authoring, or building a dashboard.
4
4
 
5
- ## Domain-First Discovery
5
+ ## Group-first discovery
6
6
 
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.
7
+ 1. Read visible groups and effective status.
8
+ 2. Choose a group and confirm the audience.
9
+ 3. Explore semantic models and root metrics.
10
+ 4. Read model-owned entities, dimensions, and measures.
11
+ 5. Inspect retained rules and dashboard usage.
12
+ 6. Reuse definitions before proposing a gap.
8
13
 
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
- 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 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` carries `total`/`truncated` too - fewer rows than `total` means the list was capped, not that assets are missing; raise `--limit`. (`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
+ The lowercase group name is the semantic label. Use the server-provided uppercase `domain_keys`/status key for data-source `--domain`.
14
15
 
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. **Exception:** when a response carries `migration_incomplete: true`, the org's KB rows are mid-migration and invisible, so emptiness is not evidence of anything - report the migration state instead of proposing creation. If the user declines ("just build it", "skip KB"), respect it and work from the table schema - read its columns with `graphit kb explore table <NAME>`.
16
+ ## Choosing the object
16
17
 
17
- ## Metric vs Dimension
18
+ | Need | Object |
19
+ |---|---|
20
+ | Modeled relation, grain, joins, columns | semantic model |
21
+ | Join identity or grain | entity inside a model |
22
+ | Grouping/filter field | dimension inside a model |
23
+ | Aggregation input | measure inside a model |
24
+ | Reusable business calculation | metric |
25
+ | Organizational placement | group |
26
+ | Governance guidance/enforcement | rule |
27
+ | D7/D30 or gross/net variant | concrete family member |
18
28
 
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) |
29
+ Entities replace relationship assets. Topics are metadata, not roots. Synonyms are removed; search names/descriptions and ask when wording is ambiguous.
26
30
 
27
- ## When to Propose KB Asset Creation
31
+ ## Read roles
28
32
 
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 |
33
+ - `list` inventories a root noun.
34
+ - `tree` shows the collapsed hierarchy.
35
+ - `search` covers models, metrics, groups, and nested components; not retained rules.
36
+ - `get` reads one exact root.
37
+ - `entity` reads one entity across visible declaring models.
38
+ - `family` expands/resolves concrete members.
39
+ - `explore` accepts semantic-model, metric, or group.
40
+ - `usage` answers dashboard placement and rule impact.
35
41
 
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?"
42
+ A capped or empty search is not proof of absence.
37
43
 
38
- ## Naming Conventions
44
+ ## Governed references
39
45
 
40
- All KB assets are UPPER_SNAKE_CASE (auto-sanitized on write):
46
+ Use `{{ Metric('revenue') }}`, `{{ Dimension('order__channel') }}`, and `{{ Measure('order_total') }}` only for Graphit's measure extension. Legacy token grammar is refused.
41
47
 
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
+ ## Gap decision
48
49
 
49
- > `*_RATE`/ratio columns are usually 0-1 fractions. To chart them as a percent, multiply by 100 in SQL (`* 100.0`) - `"percent"` format appends `%` without scaling.
50
-
51
- ## Formula Syntax
52
-
53
- Metrics reference `TABLE.COLUMN` with UPPERCASE naming:
54
-
55
- ```sql
56
- -- Metric formulas (aggregation required)
57
- SUM(ORDERS.AMOUNT)
58
- COUNT(DISTINCT EVENTS.USER_ID) WHERE EVENTS.EVENT_TS >= DATEADD(day, -30, CURRENT_DATE)
59
- SUM(ORDERS.REVENUE) / NULLIF(SUM(ORDERS.COST), 0)
60
-
61
- -- Dimension formulas (no aggregates)
62
- EVENTS.PLATFORM
63
- DATE_TRUNC('month', EVENTS.EVENT_TS)
64
- CASE WHEN USERS.AGE >= 18 THEN 'adult' ELSE 'minor' END
65
- ```
66
-
67
- Use CASE WHEN for conditionals (Snowflake has no FILTER WHERE). Always guard division with NULLIF.
68
-
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
-
71
- ## Reuse Over Reinvention
72
-
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
-
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.
76
-
77
- **Where is a known asset already shown?** `explore` shows *where* ambiently (`presented_on` lists the dashboards); `graphit kb usage metric REVENUE` is the directed deep dive: per-chart hits - reuse them, don't rebuild. `--metric X --dimension Y` finds co-occurrence (not a proven `GROUP BY`); only governed `{{metric:}}`/`{{dim:}}` usage is indexed. `graphit kb usage rule X` lists every chart a rule is enforced on (impact analysis).
78
-
79
- 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`.
50
+ Propose authoring only when no visible definition fits and business meaning is clear. State formula, grain, binding, group, rule impact, and verification. Ask when any is ambiguous.
@@ -1,23 +1,25 @@
1
- # KB Scope: Who Will See It
1
+ # KB Scope
2
2
 
3
- Consult BEFORE creating or moving any KB asset, and whenever the user asks who can see something. A domain is not only a filing shelf, it is the access boundary, so choosing one is choosing an audience - and the choice is not freely reversible.
3
+ Load when deciding who may see or change semantic work.
4
4
 
5
- | Scope | Who reads it | Use it for |
6
- |---|---|---|
7
- | The org commons | Everyone in the organization. It always exists, cannot be deleted, and is where an org-wide asset belongs | Definitions the whole company shares, and anything with no narrower home |
8
- | A shared business domain | Whoever has been granted it. `graphit status` shows the caller's own access, advisory only - the server decides | The normal case: work a team owns and reuses |
9
- | A private space | Its owner alone. Invisible to everyone else, admins included, and shown simply as "Private" | Scratch work, or data someone is not ready to share |
5
+ ## Two names, two purposes
10
6
 
11
- **Settle the scope with the user before creating anything.** Ask which of these the work belongs in rather than inferring it, and say plainly what the choice means: work put in a private space will not appear for their team at all. Default to a shared domain (or the commons) for anything the team should be able to use.
7
+ - **Group name:** lowercase semantic placement, such as `finance`.
8
+ - **Policy domain key:** uppercase access key returned by status or `domain_keys`, such as `FINANCE`. Data-source `--domain` uses this key.
12
9
 
13
- If a create or update response carries a `visibility` notice, the asset landed where only its author can read it. Relay the notice, and if the user meant the work for their team, treat that as the moment to fix the scope - not a detail to skip past.
10
+ Never invent the key when the server returned it.
14
11
 
15
- **It is not freely reversible**, which is why the question comes first:
12
+ ## Visibility
16
13
 
17
- - An asset whose DEFINITION reads a private table (a metric's dependencies, a synonym's canonical target, a relationship's tables, or a table's own home) is readable by its author alone. That is by design, and it stays that way.
18
- - Redefining an ALREADY-SHARED asset onto a private table is refused, because it would remove a working asset from the team without telling them. Copy it into the private space instead and change the copy.
19
- - ATTACHING a private table to a shared asset as a placement (a secondary table, a rule target, an extra domain) is fully supported: the asset stays shared and usable, and nobody else sees the private reference. This is the safe way to enrich a team asset with something personal.
14
+ - Org commons is a synthetic shared scope.
15
+ - A private workspace is visible only to its owner; admins are concealed too.
16
+ - Hidden and missing assets are the same absence.
17
+ - Models and metrics inherit visibility from group placement.
18
+ - Group `access` is dbt metadata and does not grant Graphit access.
19
+ - Rules use server-owned target-derived domain keys.
20
20
 
21
- **When a delete is refused over someone's private dependency.** Deleting a shared asset can be blocked because another member's PRIVATE asset depends on it. The refusal names the owners to go ask - never their assets - and that attribution is the point: relay who is named and suggest asking them first. `--force` on `kb delete` proceeds anyway (it is offered only when every hidden blocker has a named owner), and every named owner is notified of what was deleted, by whom, and what of theirs broke. Treat `--force` as the user's deliberate escalation: never add it on your own to get past a refusal.
21
+ ## Writes
22
22
 
23
- Never read a private space's stored name aloud or invent one. Refer to it as the user's private space. To target it in a command, pass `--domain Private` - the alias always resolves to the caller's OWN space, so no spelling of it can reach anyone else's.
23
+ Read access is the ceiling. A user also needs `kb_write` for the affected key; group lifecycle is admin-only. Moving an asset requires authority over current and destination scopes.
24
+
25
+ Before authoring confirm audience, group, policy key, shared/private scope, and write capability. Never name concealed groups, assets, targets, or counts.
@@ -1,68 +1,42 @@
1
- # KB Structure (Plan)
1
+ # KB Structure
2
2
 
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.
3
+ Load when planning, explaining, or locating semantic assets.
4
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).
5
+ ## Root model
6
6
 
7
- ## Node Types
8
-
9
- | Type | Description |
10
- |------|-------------|
11
- | metric | Aggregation formula (SUM, COUNT, AVG) that computes a business KPI |
12
- | dimension | Row-level SQL expression on one table, used for grouping or filtering |
13
- | rule | Free-text business constraint, optionally scoped to one or more tables |
14
- | synonym | Maps a business term to a canonical metric, dimension, or column |
15
- | table | Physical data location in the warehouse, with typed columns |
16
- | topic | Business-concept tag applied to assets (e.g., REVENUE, ACQUISITION) |
17
- | domain | High-level business area (e.g., MARKETING, SALES, PRODUCT), and the boundary that decides who can see what sits in it |
18
- | relationship | Documented JOIN pattern between two tables |
19
- | memory | Org-level context notes, always global scope |
20
-
21
- ## Edge Types
22
-
23
- | Edge | From | To | Meaning |
24
- |------|------|----|---------|
25
- | depends_on | metric, dimension | table | Asset's SQL references columns in this table |
26
- | tagged_with | metric, dimension, rule, synonym | topic | Asset carries this business-concept tag |
27
- | 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 |
28
- | joins | relationship | table, table | Two tables have a documented JOIN on specific columns |
29
- | references | rule, synonym | table, column | Rule or synonym references a specific table or column name |
30
-
31
- ## How Membership Works
32
-
33
- Two different axes organize the KB - keep them separate:
34
-
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.
7
+ | Type | Meaning |
8
+ |---|---|
9
+ | group | Organizational placement; Graphit access still comes from profiles and policy keys |
10
+ | semantic model | One modeled relation and the aggregate root for entities, dimensions, and measures |
11
+ | metric | Reusable simple, ratio, or derived calculation |
12
+ | rule | Retained Graphit governance object targeting semantic identities |
37
13
 
38
- Other placements layered on top:
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.
14
+ Nested components are not independent CRUD roots:
42
15
 
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.
16
+ - **Entity:** join/grain identity. Primary or unique entities prove the keyed side; foreign entities connect models.
17
+ - **Dimension:** model-owned grouping/filter field. Address cross-model dimensions as `entity__dimension`.
18
+ - **Measure:** model-owned aggregation input used by simple metrics.
44
19
 
45
- A domain is also the access boundary: it decides who can see what sits in it. Settle that with the user before creating anything - see kb-scope.md.
20
+ A metric family is a set of concrete metrics carrying the same family name plus axis values. It is not a template asset.
46
21
 
47
- ## Tree Rendering Order
22
+ ## Relationships
48
23
 
49
- 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.
24
+ - A group's models and metrics are placed by their `group` field.
25
+ - A semantic model owns all nested components.
26
+ - Metrics reach models through referenced measures and metrics.
27
+ - Models join through shared entities. There is no relationship asset.
28
+ - Rules may target a model, entity, dimension, metric, or group.
29
+ - Topics remain metadata in `meta.graphit`; they are not roots.
30
+ - Dashboard usage is a reverse lookup, not a stored placement.
50
31
 
51
- ## Answering Structural Questions (Tier 1)
32
+ ## Names and visibility
52
33
 
53
- Structural questions have direct answers - give them in business terms:
34
+ Semantic names are lowercase snake case. Double underscore is reserved for qualified dimensions. A group name is displayed lowercase; data-access status exposes the uppercase policy key used by data-source `--domain`.
54
35
 
55
- | Question | Answer pattern |
56
- |---|---|
57
- | What is a topic / domain / table / relationship? | Use the Node Types descriptions above, in plain language |
58
- | Why is [metric] under [table]? | Its SQL depends on that table's columns (or it is referenced there via `secondary_tables`, shown as `*`) |
59
- | Why is [asset] in [topic]? | Someone tagged it - topics are manual, not derived. Check its topics list |
60
- | Why is [asset] in [domain]? | Its primary table's domain cascades to it; it was not tagged directly |
61
- | What is a relationship? | A documented JOIN between two tables (which columns, which join type) used to build correct cross-table SQL |
62
- | What is memory? | Org-level context notes (goals, terminology, conventions), global scope, shown at the tree root |
36
+ Concealment is absence. A hidden asset is exactly like a missing one. Private workspaces are visible only to their owner, including from admins.
63
37
 
64
- Verify against actual KB state when the question names a specific asset (`graphit kb get`, `graphit kb explore`).
38
+ ## Tree and graph
65
39
 
66
- ## Semantic Questions (Tier 2 - Different Handling)
40
+ Tree order is group → semantic model → nested components, with root metrics and rules alongside. Families collapse in tree/search/family views; `list metric` returns concrete metrics.
67
41
 
68
- 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.
42
+ Graph edges show group placement, model ownership, entity joins, metric composition, governance, and dashboard usage. Use exploration for semantic reach and usage for reverse dashboard impact.
@@ -1,107 +1,35 @@
1
- # KB Traversal - Worked Examples
1
+ # KB Traversal
2
2
 
3
- Contents: Which Command (the read-command picker) - Common Queries (worked examples) - Reading the Results - Presenting KB Results (the per-command output templates).
3
+ Load when investigating semantic reach or presenting KB results.
4
4
 
5
- Which `graphit kb` read command answers each question, and how to present the result. Each verb owns one role: `graphit kb list <type>` is the **inventory** (how many, and which, assets of a type exist), `graphit kb get <type> NAME` is one asset's **full definition**, and `graphit kb explore <type> NAME` is the **relationships** call - it walks the graph around one entity and returns its whole neighborhood (and, for a template, its concrete variants) in one call. `graphit kb search "<query>"` is the fallback for semantic discovery when you cannot name the entity. Explore is the primary discovery call.
5
+ ## Read roles
6
6
 
7
- ## Which Command
8
-
9
- | Question | Command |
7
+ | Need | Read |
10
8
  |---|---|
11
- | How many / which assets of a type exist (inventory) | `graphit kb list <type>` (metrics collapse to templates; `--include-variants` for the flat set) |
12
- | What is inside a domain or topic (the build entry point) | `graphit kb explore domain NAME` / `graphit kb explore topic NAME` |
13
- | What connects to X (dependencies, joins, topics, domain) | `graphit kb explore <type> NAME` |
14
- | A template's concrete variants | `graphit kb explore metric NAME` |
15
- | Get one asset's full details | `graphit kb get metric NAME` |
16
- | Find things like X when you cannot name it (semantic) | `graphit kb search "X"` (add `--type metric` to narrow) |
17
-
18
- `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.
19
-
20
- ## Common Queries
21
-
22
- ### "Show me everything in the MARKETING domain"
23
- `graphit kb explore domain MARKETING` - returns the domain's tables and the assets on them. This is the first call when scoping a build.
24
-
25
- ### "Find all revenue metrics"
26
- `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.
27
-
28
- ### "What depends on the ORDERS table?"
29
- `graphit kb explore table ORDERS` - returns the table's column schema (names, types, descriptions) plus `metric_count`/`dimension_count`/`rule_count` and every bound metric (collapsed to templates, each with a `variant_count`), dimension, and rule whose SQL references ORDERS. The counts include child variants; the lists stay collapsed.
30
-
31
- ### "What columns does this table or data source have?" (read the schema)
32
- `graphit kb explore table <NAME>` (or `graphit kb get table <NAME>`) returns the column schema - names, types, descriptions - straight from the knowledge base. This is how you read a table's schema; never `DESCRIBE` it (only read-only SELECT runs through `graphit query`, so DESCRIBE/SHOW/DDL are rejected). If a data source was just created and not yet scanned, the KB has no columns for it yet - read its shape with `graphit query "SELECT * FROM <NAME> LIMIT 0" --ds <NAME>`, or run `graphit ds verify <id>` to scan it into the KB.
33
-
34
- ### "What joins with MARKETING_UA_DS?"
35
- `graphit kb explore table MARKETING_UA_DS`, then read the relationships in the response (the documented JOINs involving that table).
36
-
37
- ### "What topics does ARPU_D1 belong to?"
38
- `graphit kb explore metric ARPU_D1` - the response includes its topics, along with its tables, dimensions, and home domain.
39
-
40
- ### "What domains do we have?" (enumerate names)
41
- `graphit kb list domains` - returns every domain with its description and asset count. 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
-
43
- ## Presenting KB Results
44
-
45
- 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.
46
-
47
- **After `graphit kb list <type>`** - count summary + table with bold names. The list is the **inventory**, and the response carries `total`/`truncated`: if the rows shown are fewer than `total`, the list was capped - raise `--limit` (a missing asset may be truncated, not absent). For metrics the list is **collapsed** - each row is a template or a flat metric, never a child variant; a template's `variant_count` says how many concrete variants it owns. Use `--include-variants` for the full flat set, or `graphit kb explore metric NAME` to enumerate one template's variants.
48
-
49
- ~~~
50
- **12 metrics** (10 flat + 2 templates, 38 variants) across 3 tables:
51
-
52
- | Metric | Table | Calculation | Params | Variants |
53
- |---|---|---|---|---|
54
- | **CPI** | **MARKETING_UA** | `SUM(spend)/SUM(installs)` | - | - |
55
- | **ARPU** | **MARKETING_UA** | `SUM(revenue)/COUNT(...)` | DAY | 18 |
56
- | **RETENTION** | **PLAYER_QUALITY** | `COUNT(CASE WHEN ...)` | DAY | 20 |
57
-
58
- To use a variant, reference `{{metric:ARPU(DAY=7)}}`; to list them, explore the template.
59
- ~~~
60
-
61
- Adapt columns per type: dimensions include semantic type, rules include constraint count, domains include asset count.
62
-
63
- **After `graphit kb get <type> <name>`** - entity heading + key-value table:
64
-
65
- ~~~
66
- ### **CPI** (metric, verified)
67
-
68
- *Cost per install*
69
-
70
- | | |
71
- |---:|---|
72
- | **Table** | **MARKETING_UA** |
73
- | **Calculation** | `SUM(spend) / SUM(installs)` |
74
- | **Parameters** | none |
75
- | **Topics** | ACQUISITION, SPEND |
76
- | **Default dims** | MEDIA_SOURCE, CAMPAIGN_NAME |
77
- ~~~
9
+ | Inventory roots | list semantic-model, metric, group, or rule |
10
+ | Full root definition | get |
11
+ | Collapsed hierarchy | tree |
12
+ | Ranked discovery | search |
13
+ | Entity declarations | entity |
14
+ | Family expansion/axis resolution | family |
15
+ | Semantic neighborhood | explore semantic-model, metric, or group |
16
+ | Dashboard/rule impact | usage |
78
17
 
79
- Adapt fields per type. Rules: content, constraints, apply-on, plus `enforced` (Enforced = has constraints / applied to every query; Suggested = body-only / guides the AI) and `governs` (the tables it covers whole + any narrowed metric/dimension targets). Metrics and dimensions carry `governed_by` - the rules governing that asset, each tagged `enforced` and a `scope_tag` (`whole table` vs `this metric`/`this dimension`) - so reading a metric also shows what rules apply to it. Dimensions also: expression, semantic type, output type.
18
+ `list metric` is flat. Families collapse in tree/search/family views.
80
19
 
81
- **After `graphit kb search`** - result count + table with type column:
20
+ ## Investigations
82
21
 
83
- ~~~
84
- **5 results** for "revenue":
22
+ - **Everything in finance:** explore group `finance`; present models, metrics/families, nested counts, and rules.
23
+ - **How revenue is defined:** get metric `revenue`, then explore it for reached models and composition.
24
+ - **What a model owns:** get the model and present entities, dimensions, measures, group, binding, and rules.
25
+ - **What joins models:** inspect shared entities. Never look for a relationship asset.
26
+ - **Where a metric is shown:** use usage.
27
+ - **Physical columns:** use metadata discovery or model physical detail; do not explore a table noun.
85
28
 
86
- | Type | Name | Description |
87
- |---|---|---|
88
- | metric | **TOTAL_REVENUE** | Total revenue across all channels |
89
- | dimension | **REVENUE_BUCKET** | Revenue range segmentation |
90
- | synonym | GMV | Maps to **TOTAL_REVENUE** (metric) |
91
- ~~~
29
+ ## Families
92
30
 
93
- **After `graphit kb explore`** - tree with bold names, indented by level:
31
+ A family card is a view over concrete metrics. If axes match several members, show the open axes and ask. Never guess.
94
32
 
95
- ~~~
96
- **CPI** (metric) on **MARKETING_UA**:
97
- - **Calculation:** `SUM(spend) / SUM(installs)`
98
- - **Tables:** **MARKETING_UA**, **MARKETING_UA_6MO** (secondary)
99
- - **Related dimensions (8):**
100
- - **MEDIA_SOURCE** - `media_source` (categorical)
101
- - **CAMPAIGN_NAME** - `campaign_name` (categorical)
102
- - **Rules:** **EXCLUDE_ORGANIC** (filters organic installs)
103
- - **Domain:** MARKETING
104
- - **Presented on:** **Marketing Command Center** (3 graphs) - offer to open or extend
105
- ~~~
33
+ ## Presentation
106
34
 
107
- For domain exploration, show Domain > Table > Asset hierarchy as a tree, then the `presented_on` dashboards. For table exploration, list the table's columns first (`NAME - type - description`), then the metrics, dimensions, and rules defined on it.
35
+ For metrics show type, definition, group, reached models, rules, and usage. For models show nested components. For rules show body, constraints, targets, mode, and usage. Omit concealed neighbors without hinting they exist.
@@ -0,0 +1,19 @@
1
+ # Metric Families
2
+
3
+ Load when the user asks for D7/D30, gross/net, payer/all, or another named metric axis.
4
+
5
+ A family is a UX grouping over concrete standard metrics. There is no parameterized template, generated child engine, `$` parameter token, or call-with-arguments query syntax.
6
+
7
+ Each member carries:
8
+
9
+ - its own lowercase metric name and complete definition
10
+ - `meta.graphit.family`
11
+ - axis key/value pairs in `meta.graphit.axes`
12
+
13
+ Create each concrete member with the family and repeatable axis options. Tree/search/family views collapse members; `list metric` remains flat.
14
+
15
+ Use family expansion to inspect members. Supply known axes to resolve. If several candidates remain, show open axes and ask—never guess a governed metric.
16
+
17
+ Reference the resolved concrete member normally with `{{ Metric('arppu_d7') }}`.
18
+
19
+ Before deleting a member or family population, inspect metric usage. Dependency guards do not prove every canvas reference is absent.
@@ -42,7 +42,7 @@ name the exact branch you could not compare. That is a successful outcome, not a
42
42
  3. **Lift the complete template onto the owner.** The whole statement in `data-graphit-sql`, the data
43
43
  source in `data-graphit-ds`. Complete means executable and parameterized:
44
44
  - Keep every `:named` placeholder. Do not bake current filter values in.
45
- - Keep `{{metric:NAME}}` / `{{dim:NAME}}` references as they are.
45
+ - Keep final `Metric`, qualified `Dimension`, and `Measure` references intact.
46
46
  - No ellipsis, no abbreviation, real table names, full WITH clause.
47
47
  4. **Preserve the rest exactly.** `params`, `deps`, the `render` callback, any branch that picks
48
48
  different SQL, and `sourceEntityId` / `targetEntityIds` attribution all stay as they were.
@@ -87,7 +87,7 @@ one statement. Everything above still applies - the hard rules, the four signals
87
87
  correct permanent end state. Stop and say so.
88
88
  2. **Lift the stabilized statement onto the entity** as `data-graphit-sql` / `data-graphit-ds`, keeping
89
89
  `:name` placeholders for everything the filters supply - never the values one run happened to use -
90
- and keeping every `{{metric:}}` / `{{dim:}}` token exactly as written. Those tokens are what carry
90
+ and keeping every final semantic token exactly as written. Those tokens carry
91
91
  lineage once the query lives on the entity; lift a raw-expression statement and step 4 strips the
92
92
  closure off an entity that references nothing.
93
93
  3. **Prove equivalence on the four signals above,** per filter state, exactly as for a legacy migration.
@@ -1,6 +1,6 @@
1
1
  # First Run: From an Empty Workspace to a First Dashboard
2
2
 
3
- Load this when the user is signed in but the workspace is empty - `graphit kb list domains` (and `graphit ds list`) come back with nothing. That means no data is connected yet. Onboarding IS the job here, not a blocker: walk the user through it one step at a time, surfacing each result. This flow self-limits - once a data source and KB assets exist, `kb list domains` is no longer empty and you land in the normal loop instead.
3
+ Load this when the user is signed in but visible groups/models and `graphit ds list` are empty. Onboarding is the job, not a blocker: walk through it one step at a time and surface each result. Once a source and semantic assets exist, return to the normal loop.
4
4
 
5
5
  ## The arc
6
6
 
@@ -35,11 +35,12 @@ Before creating anything, ask what business question the user wants to answer. T
35
35
 
36
36
  Explain that answering the question fast needs a cached data source over the connection, not repeated live-warehouse queries.
37
37
 
38
- **A domain comes first.** Every data source is created inside a KB domain - it is what makes the source findable and grantable, and there is no uncategorized fallback. A brand-new workspace has only the org-wide commons, so check with `graphit kb list domains` and, if the user's work deserves its own area, agree a name and create it before the source:
38
+ **Scope comes first.** A semantic group organizes assets, while data-source `--domain` takes the uppercase policy key returned by `graphit status` or a group's `domain_keys`. A brand-new workspace starts with org commons. Agree the audience and group before creating the source:
39
39
 
40
40
  ```bash
41
- graphit kb list domains
42
- graphit kb create domain --name MARKETING --description "Acquisition and spend"
41
+ graphit kb list group
42
+ graphit status --json
43
+ graphit kb create group --name marketing --description "Acquisition and spend"
43
44
  ```
44
45
 
45
46
  **Read the table before you write its SQL.** You cannot author a source SELECT without knowing the columns, and guessing them wastes a round trip. Read them straight off the warehouse: