@graphit/cli 0.2.321 → 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.
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/bin/graphit +1 -1
- package/bin/graphit.ps1 +1 -1
- package/dist/api/client.js +15 -0
- package/dist/api/client.js.map +1 -1
- package/dist/commands/ds-config.js +2 -2
- package/dist/commands/ds-config.js.map +1 -1
- package/dist/commands/ds.js +2 -2
- package/dist/commands/ds.js.map +1 -1
- package/dist/commands/kb.js +347 -15
- package/dist/commands/kb.js.map +1 -1
- package/dist/commands/query.js +3 -3
- package/dist/commands/query.js.map +1 -1
- package/dist/skill-guard.js +2 -1
- package/dist/skill-guard.js.map +1 -1
- package/package.json +1 -1
- package/scripts/verb-policy-source.json +30 -102
- package/skills/graphit/SKILL.md +31 -39
- package/skills/graphit/VERSION.json +1 -1
- package/skills/graphit/references/data-source-refresh.md +27 -0
- package/skills/graphit/references/data-sources.md +27 -122
- package/skills/graphit/references/filters-advanced.md +2 -2
- package/skills/graphit/references/governance-explained.md +18 -29
- package/skills/graphit/references/governance.md +20 -83
- package/skills/graphit/references/kb-actions.md +28 -81
- package/skills/graphit/references/kb-discovery.md +35 -64
- package/skills/graphit/references/kb-scope.md +17 -15
- package/skills/graphit/references/kb-structure.md +28 -54
- package/skills/graphit/references/kb-traversal.md +24 -96
- package/skills/graphit/references/metric-families.md +19 -0
- package/skills/graphit/references/migration.md +2 -2
- package/skills/graphit/references/onboarding.md +5 -4
- package/skills/graphit/references/presentations.md +1 -1
- package/skills/graphit/references/runtime.md +6 -6
- package/skills/graphit/references/semantic-authoring.md +65 -0
- package/skills/graphit/references/sql-reference.md +10 -20
- package/dist/commands/kb-constraints.d.ts +0 -14
- package/dist/commands/kb-constraints.js +0 -53
- package/dist/commands/kb-constraints.js.map +0 -1
- package/dist/commands/kb-create.d.ts +0 -2
- package/dist/commands/kb-create.js +0 -296
- package/dist/commands/kb-create.js.map +0 -1
- package/dist/commands/kb-delete.d.ts +0 -2
- package/dist/commands/kb-delete.js +0 -37
- package/dist/commands/kb-delete.js.map +0 -1
- package/dist/commands/kb-read.d.ts +0 -2
- package/dist/commands/kb-read.js +0 -223
- package/dist/commands/kb-read.js.map +0 -1
- package/dist/commands/kb-shared.d.ts +0 -43
- package/dist/commands/kb-shared.js +0 -81
- package/dist/commands/kb-shared.js.map +0 -1
- package/dist/commands/kb-update.d.ts +0 -2
- package/dist/commands/kb-update.js +0 -240
- package/dist/commands/kb-update.js.map +0 -1
- package/skills/graphit/references/parameterized-metrics.md +0 -77
|
@@ -1,79 +1,50 @@
|
|
|
1
1
|
# KB Discovery
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Load before querying, authoring, or building a dashboard.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Group-first discovery
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16
|
+
## Choosing the object
|
|
16
17
|
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
31
|
+
## Read roles
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
42
|
+
A capped or empty search is not proof of absence.
|
|
37
43
|
|
|
38
|
-
##
|
|
44
|
+
## Governed references
|
|
39
45
|
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1
|
+
# KB Scope
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Load when deciding who may see or change semantic work.
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
10
|
+
Never invent the key when the server returned it.
|
|
14
11
|
|
|
15
|
-
|
|
12
|
+
## Visibility
|
|
16
13
|
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
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
|
-
|
|
21
|
+
## Writes
|
|
22
22
|
|
|
23
|
-
|
|
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
|
|
1
|
+
# KB Structure
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Load when planning, explaining, or locating semantic assets.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Root model
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
|
10
|
-
|
|
11
|
-
| metric |
|
|
12
|
-
|
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
##
|
|
22
|
+
## Relationships
|
|
48
23
|
|
|
49
|
-
|
|
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
|
-
##
|
|
32
|
+
## Names and visibility
|
|
52
33
|
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
38
|
+
## Tree and graph
|
|
65
39
|
|
|
66
|
-
|
|
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
|
-
|
|
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
|
|
1
|
+
# KB Traversal
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Load when investigating semantic reach or presenting KB results.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Read roles
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
| Question | Command |
|
|
7
|
+
| Need | Read |
|
|
10
8
|
|---|---|
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
18
|
+
`list metric` is flat. Families collapse in tree/search/family views.
|
|
80
19
|
|
|
81
|
-
|
|
20
|
+
## Investigations
|
|
82
21
|
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 `
|
|
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
|
|
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
|
|
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
|
-
**
|
|
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
|
|
42
|
-
graphit
|
|
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:
|
|
@@ -77,7 +77,7 @@ deck.slide({
|
|
|
77
77
|
html: `
|
|
78
78
|
<h2>Live Data</h2>
|
|
79
79
|
<div data-graphit-id="spend-chart" data-graphit-label="Ad Spend"
|
|
80
|
-
data-graphit-sql="SELECT {{
|
|
80
|
+
data-graphit-sql="SELECT {{ Dimension('campaign__media_source') }} AS source, {{ Metric('total_ad_spend') }} AS spend FROM MARKETING_UA_DS GROUP BY 1 ORDER BY spend DESC LIMIT 6"
|
|
81
81
|
data-graphit-ds="MARKETING_UA_DS">
|
|
82
82
|
<div id="chart1" class="gh-loading">
|
|
83
83
|
<div class="gh-loading-overlay"><svg class="gh-loading-spin" width="24" height="24" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="#e5e5e5" stroke-width="2.5"/><path d="M12 2a10 10 0 0 1 10 10" stroke="#4DB6AC" stroke-width="2.5" stroke-linecap="round"/></svg></div>
|
|
@@ -30,7 +30,7 @@ const result = await graphit.resolve({
|
|
|
30
30
|
|
|
31
31
|
MUST: every resolve feeding a rendered graph, KPI, or table carries attribution - `target` (the entity wrapper or an element inside it), or `sourceEntityId` plus `targetEntityIds` when one result feeds several graphs. Attribution records the live filtered query behind that entity's details panel; unattributed, the panel shows `data-graphit-sql` as an unrun example, so a user changing a filter sees the SQL never move. Saving a page with filters or params and zero attributed resolves returns an `unattributed_resolves` warning. Queries feeding page chrome need no attribution and no entity, but belong on the primitives - `graphit.cascade` for option lists, `graphit.dataBounds` for a column's min/max, `graphit.rank` for top-N (`filters-advanced.md`) - never hand-written entity-less SQL.
|
|
32
32
|
|
|
33
|
-
CRITICAL: use
|
|
33
|
+
CRITICAL: use `{{ Metric('name') }}`, `{{ Dimension('entity__name') }}`, or Graphit's `{{ Measure('name') }}` extension inside `data-graphit-sql` whenever the semantic asset exists. The server expands it into the governed tier. See `governance.md`.
|
|
34
34
|
|
|
35
35
|
Error handling: `graphit.resolve()` rejects on timeout (120s), bad SQL, or an invalid data source ID. Wrap calls in try/catch and show a user-visible error in the target element on failure. Verify the SQL returns data via the CLI before embedding it.
|
|
36
36
|
|
|
@@ -41,7 +41,7 @@ Every visible element - chart, KPI card, table, text section - must be wrapped s
|
|
|
41
41
|
```html
|
|
42
42
|
<div data-graphit-id="revenue-trend"
|
|
43
43
|
data-graphit-label="Revenue Trend"
|
|
44
|
-
data-graphit-sql="SELECT {{
|
|
44
|
+
data-graphit-sql="SELECT {{ Dimension('order__region') }} AS region, {{ Metric('revenue') }} AS revenue FROM ORDERS_DS GROUP BY region"
|
|
45
45
|
data-graphit-ds="ORDERS_DS">
|
|
46
46
|
<!-- chart, KPI, or table content here -->
|
|
47
47
|
</div>
|
|
@@ -55,7 +55,7 @@ Every visible element - chart, KPI card, table, text section - must be wrapped s
|
|
|
55
55
|
| `data-graphit-ds` | Data source name (same as the FROM table) or id | `"ORDERS_DS"` |
|
|
56
56
|
| `data-graphit-state` | Not an entity attribute - a SIBLING wrapper around a user-changeable control, naming the state key saved views capture (`filters.md`) | `"country"` |
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
Semantic references are derived automatically from the final grammar; the compiler resolves them and shows asset chips in the details panel. Missing any one attribute breaks the entity; missing the wrapper makes the element invisible to the platform.
|
|
59
59
|
|
|
60
60
|
**JavaScript may populate an entity, never create one.** Every entity - including on hidden tabs, collapsed panels, and lazily-shown views - exists as static markup in the HTML you save; JavaScript fills its chart host, wires listeners, and toggles visibility. Nothing server-side runs your JavaScript, so a card built at render time (`host.innerHTML = charts.map(...)`) does not exist for governance, lineage, the KB graph, `list-entities`, or a later `get-entity`. A save whose `data-graphit-id`s are reachable only by running your JavaScript is REFUSED and the error names them. A dynamic **query** is supported - the attribute carries the canonical template; a dynamic **entity** is not.
|
|
61
61
|
|
|
@@ -74,14 +74,14 @@ A filtered entity uses `graphit.bind(el, { params, deps, render })` (`filters.md
|
|
|
74
74
|
|
|
75
75
|
```html
|
|
76
76
|
<div data-graphit-id="explorer" data-graphit-label="Metric Explorer" data-graphit-ds="UA_DS"
|
|
77
|
-
data-graphit-sql="SELECT day, {{
|
|
78
|
-
data-graphit-vocab="metric:
|
|
77
|
+
data-graphit-sql="SELECT day, {{ Metric('roas') }} AS roas FROM UA_DS GROUP BY 1"
|
|
78
|
+
data-graphit-vocab="metric:revenue,metric:roas,dimension:campaign__country"></div>
|
|
79
79
|
```
|
|
80
80
|
```js
|
|
81
81
|
graphit.resolve({ sql: buildSql(picked), dataSourceId: "UA_DS", runtimeComposed: true, sourceEntityId: "explorer" });
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
Use comma-separated lowercase declarations such as `metric:revenue`, `dimension:order__region`, and `measure:order_total`. Every declared name must exist; unknown names and wildcards refuse. Undeclared inline SQL is invisible to lineage and governance. Existing runtime-composed queries are first-class, not debt; migrate query ownership only when asked (`migration.md`).
|
|
85
85
|
|
|
86
86
|
**Label equals the visible title.** `data-graphit-label` MUST match the card's visible heading exactly - users find their chart by that label in @ mention dropdowns and entity panels, and a mismatch means they cannot find it.
|
|
87
87
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Semantic Authoring
|
|
2
|
+
|
|
3
|
+
Load when creating or changing semantic models or metrics.
|
|
4
|
+
|
|
5
|
+
## Syntax boundary
|
|
6
|
+
|
|
7
|
+
Graphit accepts the MetricFlow 0.211 execution shape: semantic models contain
|
|
8
|
+
entities, dimensions, and measures; metrics are top-level objects with `type`
|
|
9
|
+
and `type_params`. Do not emit newer measureless/Fusion authoring syntax, dbt
|
|
10
|
+
project YAML, Jinja, `ref()` expressions, or source declarations on this
|
|
11
|
+
surface. Those belong to dbt project import/export, not Graphit authoring.
|
|
12
|
+
|
|
13
|
+
Public dbt/MetricFlow concepts:
|
|
14
|
+
|
|
15
|
+
- semantic model, entity, dimension, measure, metric, and group
|
|
16
|
+
- simple, ratio, and derived metric composition
|
|
17
|
+
- entity-qualified dimension paths
|
|
18
|
+
|
|
19
|
+
Graphit extensions and assets:
|
|
20
|
+
|
|
21
|
+
- `meta.graphit.family` and `axes` group concrete metric variants
|
|
22
|
+
- topics are curated Graphit metadata, deliberately not dbt `tags`
|
|
23
|
+
- rules are separate Graphit objects targeting semantic identities by name;
|
|
24
|
+
never embed them in dbt metadata
|
|
25
|
+
- verification attribution, provenance, and data-source bindings are
|
|
26
|
+
server-owned; use dedicated actions instead of hand-authoring them
|
|
27
|
+
|
|
28
|
+
Do not expose or depend on storage collection names, revision fields, feature
|
|
29
|
+
flags, cache keys, or compiler implementation details.
|
|
30
|
+
|
|
31
|
+
## Semantic model
|
|
32
|
+
|
|
33
|
+
A semantic model owns:
|
|
34
|
+
|
|
35
|
+
- lowercase name and physical model/binding
|
|
36
|
+
- group placement
|
|
37
|
+
- primary grain/entity
|
|
38
|
+
- entities for joins
|
|
39
|
+
- dimensions for grouping/filtering
|
|
40
|
+
- measures for aggregation input
|
|
41
|
+
- defaults such as aggregation time dimension
|
|
42
|
+
|
|
43
|
+
Measure-bearing models need a valid aggregation time dimension. Primary/unique entity claims require grain evidence; never guess uniqueness. Time-aware shapes require the platform time-spine prerequisite.
|
|
44
|
+
|
|
45
|
+
Nested lists replace whole lists on update. Read the model and preserve every sibling.
|
|
46
|
+
|
|
47
|
+
## Metrics
|
|
48
|
+
|
|
49
|
+
Only these shapes are served by the shipping fragment path:
|
|
50
|
+
|
|
51
|
+
- **simple:** one measure object
|
|
52
|
+
- **ratio:** numerator and denominator metric objects
|
|
53
|
+
- **derived:** expression over declared metric inputs or aliases
|
|
54
|
+
|
|
55
|
+
Metric-level and per-input filters must resolve through declared semantic identities. Bare strings where an input object is required are refused.
|
|
56
|
+
|
|
57
|
+
Cumulative, conversion, shifted inputs, time-spine joins, and null-fill are unavailable until Project #289. Use a supported decomposition or clearly labeled free SQL; do not create an unusable governed definition.
|
|
58
|
+
|
|
59
|
+
## Verification
|
|
60
|
+
|
|
61
|
+
Create defaults to verified on human-driven CLI paths; `--unverified` creates a draft. Promote or demote with dedicated verify/unverify actions. Never replace `meta` only to toggle verification.
|
|
62
|
+
|
|
63
|
+
## Final check
|
|
64
|
+
|
|
65
|
+
Re-read the root, verify nested completeness, inspect group/binding, and use the returned receipt. A degraded freshness result may mean the write landed; do not retry blindly.
|