@graphit/cli 0.2.348 → 0.2.351

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.
@@ -9,7 +9,7 @@
9
9
  //
10
10
  // Requires a prior `npm run build` (it imports the compiled registrars from dist/).
11
11
 
12
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
12
+ import { existsSync, readFileSync, writeFileSync, writeSync } from "node:fs";
13
13
  import { join } from "node:path";
14
14
  import {
15
15
  buildProgram,
@@ -28,6 +28,23 @@ const skillPath = join(cliRoot, "skills", "graphit", "SKILL.md");
28
28
  const START = "<!-- COMMANDS:START -->";
29
29
  const END = "<!-- COMMANDS:END -->";
30
30
 
31
+ /**
32
+ * Feature #814, mirroring `writeStderr` in cli/src/stderr.ts.
33
+ *
34
+ * `console.error` is asynchronous when stderr is a pipe - which is how CI and
35
+ * every agent runs this generator - so a `process.exit()` in the same tick
36
+ * discards the buffered line. The remediation banner below is the whole value of
37
+ * a failing check run, so it is written synchronously; the guard in
38
+ * cli/test/lint-write-before-exit.test.mjs covers cli/src, not cli/scripts.
39
+ */
40
+ function writeStderr(message) {
41
+ try {
42
+ writeSync(2, message.endsWith("\n") ? message : `${message}\n`);
43
+ } catch {
44
+ // stderr closed or busy - a lost log line is never worth failing a build.
45
+ }
46
+ }
47
+
31
48
  function esc(text) {
32
49
  return String(text ?? "").replace(/\r?\n/g, " ").replace(/\|/g, "\\|").trim();
33
50
  }
@@ -51,14 +68,23 @@ function renderBlock(program) {
51
68
  flags: formatFlags(verb.options),
52
69
  });
53
70
  }
71
+ // The prose above the markers in SKILL.md already says the table is generated
72
+ // and to check `--help` for flags; this line only marks the block as generated.
54
73
  const lines = [
55
74
  "",
56
- "_Generated from the CLI by `npm run gen:commands` - do not hand-edit between the markers. Run `graphit <cmd> --help` for exact flag values and descriptions._",
75
+ "_Generated by `npm run gen:commands`; do not hand-edit between the markers._",
57
76
  "",
58
77
  ];
59
78
  for (const name of groups.keys()) {
60
79
  const { description, rows } = groups.get(name);
61
- lines.push(`**${name}**${description ? ` - ${esc(description)}` : ""}`);
80
+ // A self-invocable group (`status`, `query`, `setup`) is its own single row,
81
+ // and Commander gives the group and the row the same description. Print it
82
+ // once, on the row - the row is what the tests pin and what an agent reads.
83
+ // SKILL.md is always-loaded and sits on a hard size ceiling, so a repeated
84
+ // description is paid for on every turn.
85
+ const groupDescription =
86
+ rows.length === 1 && rows[0].description === description ? "" : description;
87
+ lines.push(`**${name}**${groupDescription ? ` - ${esc(groupDescription)}` : ""}`);
62
88
  for (const row of rows) {
63
89
  const parts = [`- \`${row.command}\``];
64
90
  if (row.description) parts.push(esc(row.description));
@@ -90,27 +116,23 @@ const program = await buildProgram();
90
116
  const block = renderBlock(program);
91
117
 
92
118
  if (toStdout) {
119
+ // No process.exit() after this write: on a pipe, exiting before stdout drains
120
+ // truncates the block, and callers diff it whole.
93
121
  process.stdout.write(block);
94
- process.exit(0);
95
- }
96
-
97
- // Normalize CRLF before compare so Windows checkouts don't report perpetual drift.
98
- const current = existsSync(skillPath)
99
- ? readFileSync(skillPath, "utf-8").replace(/\r\n/g, "\n")
100
- : "";
101
- const next = injectBetweenMarkers(current, block);
122
+ } else {
123
+ // Normalize CRLF before compare so Windows checkouts don't report perpetual drift.
124
+ const current = existsSync(skillPath)
125
+ ? readFileSync(skillPath, "utf-8").replace(/\r\n/g, "\n")
126
+ : "";
127
+ const next = injectBetweenMarkers(current, block);
102
128
 
103
- if (current === next) {
104
- console.error("Command table in SKILL.md is in sync.");
105
- process.exit(0);
106
- }
107
-
108
- if (checkOnly) {
109
- console.error(
110
- "SKILL.md command table is stale - run: npm run gen:commands",
111
- );
112
- process.exit(1);
129
+ if (current === next) {
130
+ writeStderr("Command table in SKILL.md is in sync.");
131
+ } else if (checkOnly) {
132
+ writeStderr("SKILL.md command table is stale - run: npm run gen:commands");
133
+ process.exitCode = 1;
134
+ } else {
135
+ writeFileSync(skillPath, next, "utf-8");
136
+ writeStderr(`Updated command table in ${skillPath}`);
137
+ }
113
138
  }
114
-
115
- writeFileSync(skillPath, next, "utf-8");
116
- console.error(`Updated command table in ${skillPath}`);
@@ -18,7 +18,7 @@
18
18
  // - it never emits an org/user identity parameter. Tenancy is bound from the
19
19
  // authenticated session at construction, never from a model-supplied value.
20
20
 
21
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
21
+ import { existsSync, readFileSync, writeFileSync, writeSync } from "node:fs";
22
22
  import { join } from "node:path";
23
23
  import { buildProgram, cliRoot, collectVerbs } from "./commander-walk.mjs";
24
24
 
@@ -81,11 +81,29 @@ const APP_PARAM_TYPES = new Set(["string", "integer", "boolean"]);
81
81
  const errors = [];
82
82
  const fail = (msg) => errors.push(msg);
83
83
 
84
+ /**
85
+ * Feature #814, mirroring `writeStderr` in cli/src/stderr.ts.
86
+ *
87
+ * `console.error` is asynchronous when stderr is a pipe - which is how CI and
88
+ * every agent runs this generator - so a `process.exit()` in the same tick
89
+ * discards the buffered lines. The gate failures below are the whole value of a
90
+ * failing run, so they are written synchronously; the guard in
91
+ * cli/test/lint-write-before-exit.test.mjs covers cli/src, not cli/scripts.
92
+ */
93
+ function writeStderr(message) {
94
+ try {
95
+ writeSync(2, message.endsWith("\n") ? message : `${message}\n`);
96
+ } catch {
97
+ // stderr closed or busy - a lost log line is never worth failing a build.
98
+ }
99
+ }
100
+
84
101
  /** Nothing is written while a single gate is unsatisfied. */
85
102
  function exitOnErrors() {
86
103
  if (!errors.length) return;
87
- console.error("Tool manifest generation failed:");
88
- for (const e of errors) console.error(` - ${e}`);
104
+ writeStderr(
105
+ ["Tool manifest generation failed:", ...errors.map((e) => ` - ${e}`)].join("\n"),
106
+ );
89
107
  process.exit(1);
90
108
  }
91
109
 
@@ -599,10 +617,10 @@ if (toStdout) {
599
617
  if (current.manifest === nextManifest && current.policy === nextPolicy) {
600
618
  console.error("Tool manifest and verb policy are in sync.");
601
619
  } else if (checkOnly) {
602
- console.error(
620
+ writeStderr(
603
621
  "Generated tool manifest / verb policy is stale - run: npm --prefix cli run gen:commands",
604
622
  );
605
- process.exit(1);
623
+ process.exitCode = 1;
606
624
  } else {
607
625
  writeFileSync(manifestPath, nextManifest, "utf-8");
608
626
  writeFileSync(policyOutPath, nextPolicy, "utf-8");
@@ -67,6 +67,14 @@
67
67
  "requires_approval": false,
68
68
  "silent_retry_exempt": false
69
69
  },
70
+ "kb source": {
71
+ "surface": "both",
72
+ "noun": "kb_read",
73
+ "is_read_only": true,
74
+ "mutation_class": "none",
75
+ "requires_approval": false,
76
+ "silent_retry_exempt": false
77
+ },
70
78
  "kb get": {
71
79
  "surface": "both",
72
80
  "noun": "kb_read",
@@ -137,7 +145,7 @@
137
145
  "mutation_class": "none",
138
146
  "requires_approval": false,
139
147
  "silent_retry_exempt": true,
140
- "reason": "A CI/shell verb: --path walks a local checkout the app has no filesystem for, and --sha is the pipeline's job on a pull request. An in-app turn reads the synced KB through the kb_read noun; it has no commit to verify."
148
+ "reason": "A CI/shell verb: --path walks a local checkout the app has no filesystem for, --sha is the pipeline's job on a pull request, and with neither flag it plans the head of the bound branch - the admin's pre-merge check from a shell. An in-app turn reads the synced KB through the kb_read noun; it has no commit to verify, and the app's own read-only scan of the branch head is the Settings panel's, not an agent's."
141
149
  },
142
150
  "kb repo apply": {
143
151
  "surface": "cli_only",
@@ -147,6 +155,22 @@
147
155
  "silent_retry_exempt": true,
148
156
  "reason": "The merge job's verb: it applies a provider-verified commit to the shared KB under the repository binding's apply lease. Shared-KB authorship in a repository-owned org happens through a pull request, never an in-app turn, so the app must not advertise it."
149
157
  },
158
+ "kb repo status": {
159
+ "surface": "cli_only",
160
+ "is_read_only": true,
161
+ "mutation_class": "none",
162
+ "requires_approval": false,
163
+ "silent_retry_exempt": true,
164
+ "reason": "Reads the durable operation created by the CLI-only verify/apply workflow. The caller supplies its operation id from a shell or CI job; in-app turns do not create those operations."
165
+ },
166
+ "kb repo cancel": {
167
+ "surface": "cli_only",
168
+ "is_read_only": false,
169
+ "mutation_class": "kb",
170
+ "requires_approval": true,
171
+ "silent_retry_exempt": true,
172
+ "reason": "An org admin explicitly stops a saved repository apply task from a shell. This changes the CLI-only apply workflow and is not an in-app shared-KB authoring action."
173
+ },
150
174
  "kb repo show": {
151
175
  "surface": "cli_only",
152
176
  "is_read_only": true,
@@ -177,7 +201,7 @@
177
201
  "mutation_class": "kb_config",
178
202
  "requires_approval": true,
179
203
  "silent_retry_exempt": true,
180
- "reason": "Binds a provider connection and repository to the org (org admin). The connection ids and repository slug come from the admin's own tooling; an in-app turn has neither."
204
+ "reason": "Binds a provider connection and repository to the org (org admin): the one-time configuration step that decides who owns the shared KB. The admin does it deliberately from a shell alongside the repository checkout they are about to hand the org, and the app's surface for it is the Settings panel's binding form, never an agent turn. Reviewed 2026-09-06 (Issue #921): the earlier reason claimed the connection ids came from the admin's own tooling. They did not - no verb produced one on any surface, which is the gap `connector list` now closes."
181
205
  },
182
206
  "kb repo identities": {
183
207
  "surface": "cli_only",
@@ -572,7 +596,7 @@
572
596
  "mutation_class": "none",
573
597
  "requires_approval": false,
574
598
  "silent_retry_exempt": false,
575
- "reason": "Connection lifecycle remains CLI-only, but connection enumeration already exists in-app through the gated `list_warehouse_metadata` overlay tool. A ninth generated noun would duplicate that read surface and spend tool-window budget, so the Commander verb stays excluded while the platform replacement remains available."
599
+ "reason": "Connection lifecycle remains CLI-only, and warehouse enumeration already exists in-app through the gated `list_warehouse_metadata` overlay tool. A ninth generated noun would duplicate that read surface and spend tool-window budget, so the Commander verb stays excluded while the platform replacement remains available. Reviewed 2026-09-06 (Issue #921): the verb now also lists Slack and repository connections, which the overlay does not - that is deliberate. Those ids are consumed only by verbs that are themselves cli_only (`kb repo bind`, the Slack setup done in the web app), so an in-app turn has nothing to spend them on."
576
600
  },
577
601
  "connector add snowflake-keypair": {
578
602
  "surface": "cli_only",
@@ -2,10 +2,10 @@
2
2
  name: graphit
3
3
  description: >-
4
4
  Use Graphit for ANY question about the user's business or product data: metrics, KPIs, revenue, retention, spend, users, cohorts, funnels, trends, comparisons, "why did X change", "how are we doing on Y", analysis, reports, or dashboards. Activate even when the user does not say "Graphit" or name any tool: if someone wants to understand their numbers, this is the tool. Graphit answers through a governed semantic layer (computed the team's way, reusable and safe to share) and delivers the answer as a fast cached-data query or a hand-authored interactive HTML dashboard, and can create the metrics, dimensions, and rules an answer needs. Prefer Graphit over hand-rolled one-off analysis whenever the data is, or could be, the user's business data. Skip only for pure software tasks (code, logs, config, infra) or data with nothing to do with the user's business.
5
- skill_version: "0.2.348"
5
+ skill_version: "0.2.351"
6
6
  ---
7
7
 
8
- <!-- SIZE EXEMPTION (SKILL.md): standard hard limit 12,288 chars, exempted ceiling 32,000. Always-loaded: the collaboration/pace spine, hard constraints + scope gate, the loop, and the generated command table (COMMANDS markers, scripts/generate-commands-doc.mjs) - needed every turn, cannot defer to a reference. Marker sits after the frontmatter so the loader and sync-plugin-version.mjs parse it. Reviewed 2026-08-02. Raised from 29,952 on 2026-08-07 (founder-directed): a domain is now an access boundary, so loop step 2 must say that picking one decides who ever sees the work - load-bearing before any reference load can be relied on. Raised from 30,592 on 2026-08-13 (founder-directed): the living-context MUST bullet - explore placements answer path + pre-create fork. SIZING.md rules raises pay only for command-table growth; both raises are deliberate exceptions. Raised from 31,232 on 2026-08-16: the generated table gained `dashboard check` and its flags - table growth, the sanctioned kind - plus that verb's one router row. -->
8
+ <!-- SIZE EXEMPTION (SKILL.md): hard limit 12,288 chars, exempted ceiling 32,000. Reviewed 2026-09-06. Always-loaded: the collaboration/pace spine, hard constraints + scope gate, the loop, and the generated command table (COMMANDS markers; cli/scripts/generate-commands-doc.mjs) - needed every turn, not deferrable. Marker sits after the frontmatter so the loader and sync-plugin-version.mjs parse it. Raises pay only for command-table growth; each is recorded in docs/knowledge/prompt-engineering/sizing/SIZING.md, prose changes in docs/workflow/prompt-changes/INDEX.md. -->
9
9
 
10
10
  # Graphit CLI
11
11
 
@@ -92,7 +92,7 @@ Soft narration is what "just build it" drops. These hard stops hold even then: c
92
92
  ### Handoffs, failure, truthful reporting
93
93
 
94
94
  - Name the handoffs. Some actions live on the platform, not the CLI: visiting a data source's verification link, deleting a source from the Sources Hub. Say when a step hands control back to the user, and move between building the dashboard and building the knowledge base through the gate.
95
- - Keep local files ephemeral. Any file you create - scratch HTML, an export, throwaway SQL - goes in one `./.graphit/` working dir, never scattered in the repo. When the work is done, offer to remove it. Mechanics: operations.md.
95
+ - Keep scratch files together. In repository-owned workflows, `.graphit/` holds durable KB definitions: never ignore or delete it as scratch. Read repo-preparation.md before authoring those files; other local artifacts follow operations.md.
96
96
  - On failure: retry once if it looks transient (timeout, rate limit); on a real error (missing column, permission, validation) stop, say what failed and the next step, never a bare "something went wrong".
97
97
  - Report truthfully: what worked, what did not, what you are unsure of. If only part succeeded, say which part and why the rest did not. Done means the answer is delivered and every dashboard element resolves on real data with no entity_sql_warnings.
98
98
 
@@ -142,6 +142,7 @@ Load only the relevant reference. Check `graphit <command> --help` for flags.
142
142
 
143
143
  | Situation | Read |
144
144
  |---|---|
145
+ | preparing a repository-owned KB from repository docs | repo-preparation.md |
145
146
  | a brand-new or empty workspace, nothing connected yet | onboarding.md |
146
147
  | scoping to a domain, data source, and assets | kb-discovery.md, kb-traversal.md, data-sources.md |
147
148
  | a repository-owned (`manual`) org: verify, CI tokens, Data Sources via PR | references/repo-kb.md |
@@ -162,30 +163,32 @@ Load only the relevant reference. Check `graphit <command> --help` for flags.
162
163
 
163
164
  ## Commands
164
165
 
165
- Claude Code supplies the `graphit` wrapper. On Codex, Cursor, terminals and CI, use `npx -y @graphit/cli@0.2.348 <command>`; pin a version for reproducibility. The table is generated from the CLI; check command help for exact flags.
166
+ Claude Code supplies the `graphit` wrapper. On Codex, Cursor, terminals and CI, use `npx -y @graphit/cli@0.2.351 <command>`; pin a version for reproducibility. The table is generated from the CLI; check command help for exact flags.
166
167
 
167
168
  <!-- COMMANDS:START -->
168
169
 
169
- _Generated from the CLI by `npm run gen:commands` - do not hand-edit between the markers. Run `graphit <cmd> --help` for exact flag values and descriptions._
170
+ _Generated by `npm run gen:commands`; do not hand-edit between the markers._
170
171
 
171
172
  **auth** - Authentication commands
172
173
  - `auth login` - Log in to Graphit via browser
173
174
  - `auth status` - Show current authentication status
174
175
  - `auth logout` - Log out and clear stored credentials
175
176
 
176
- **status** - Show your effective permissions per domain (advisory; the server re-authorizes every operation)
177
+ **status**
177
178
  - `status` - Show your effective permissions per domain (advisory; the server re-authorizes every operation)
178
179
 
179
180
  **kb** - dbt-native Knowledge Base - semantic models with nested components, concrete metrics/families, groups, and retained rules
180
- - `kb repo verify` - Compute the plan for a .graphit/ tree: identity + access, their semantic layer through provenance, validity + completeness, and the KB/DS/dashboard actions. Any refusal exits 2 (a CI check fails). --sha pulls the commit from the bound provider (CI); --path uploads a local checkout and yields a local-only plan that can never be applied - `--sha --pr --path --allow-dirty --kind --token --poll-interval-ms --timeout-ms`
181
+ - `kb repo verify` - Plan a .graphit/ tree: identity + access, semantic layer via provenance, validity + completeness, KB/DS/dashboard actions. Any refusal exits 2 (CI fails). --sha pulls that commit from the bound provider (CI); --path uploads a checkout for a local-only plan that can never be applied; neither plans the bound branch head - `--sha --pr --path --allow-dirty --kind --token --poll-interval-ms --timeout-ms`
181
182
  - `kb repo show` - Show the repository binding: ownership mode, bound repository, branch, connection, last applied commit
182
183
  - `kb repo mode <mode>` - Set KB ownership (org admin): managed = direct writes; manual = the repository owns shared KB assets and Data Source definitions (changes via pull request). A non-empty managed KB refuses manual
183
- - `kb repo bind` - Bind the owning repository to a provider connection (org admin); the connection must name that repository - `--connection --repo --branch --approved-sha`
184
+ - `kb repo bind` - Bind the owning repository (org admin); --repo resolves the provider connection naming it unless several healthy ones match - `--connection --repo --branch --approved-sha`
184
185
  - `kb repo identities` - List git identities (org admin): linked, unlinked, and accounts the last plans could not link
185
186
  - `kb repo link-identity <provider> <account> <member-email>` - Link a provider account (login or account id) to the member with that email (org admin)
186
187
  - `kb repo unlink-identity <provider> <account>` - Unlink a provider account from its member (org admin); leaves a tombstone the silent email match cannot cross
187
188
  - `kb repo export-datasources` - Write the live Data Source definitions as datasources/<name>.ds.yml + <name>.sql (org admin): the mirror an org commits before entering manual mode; byte-identical when nothing differs - `--out`
188
189
  - `kb repo apply` - Apply a plan (org admin). --plan applies a saved plan id; --sha re-verifies the commit and applies the fresh plan in one call (the merge job). Refuses a stale plan, a changed tree, a failing verdict or a local-only plan; a concurrent apply on the same repository waits - `--plan --sha --pr --kind --token --poll-interval-ms --timeout-ms`
190
+ - `kb repo status <operation-id>` - Read a repository operation once by id; accepts the login or GRAPHIT_TOKEN
191
+ - `kb repo cancel` - Request cancellation of a saved plan (org admin login); already applied changes remain - `--plan`
189
192
  - `kb repo token mint` - Mint a display-once CI machine token (org admin); scope kb:verify or kb:apply - `--scope`
190
193
  - `kb repo token list` - List CI machine tokens: metadata only, never the secret
191
194
  - `kb repo token revoke <token-id>` - Revoke a CI machine token (org admin)
@@ -200,8 +203,9 @@ _Generated from the CLI by `npm run gen:commands` - do not hand-edit between the
200
203
  - `kb create rule` - Create a retained Graphit governance rule from JSON. Targets use model:, entity:, dimension:, metric: or group: identities - `--file --json`
201
204
  - `kb update <noun> <name>` - Update an asset with a JSON patch. On semantic-model, a provided entities/dimensions/measures list replaces the stored list whole; explicit meta replaces author metadata whole - `--file --json`
202
205
  - `kb delete <noun> <name>` - Delete an asset (requires --yes). Checks known definition dependencies; inspect usage separately for canvas impact - `--yes`
206
+ - `kb source <noun> <name>` - Read recorded definition files at the applied repository commit, with numbered lines and drift evidence - `--document --context`
203
207
  - `kb get <noun> <name>` - Fetch one asset. Metrics include their family and sibling variants
204
- - `kb list <noun>` - List all visible assets of one noun
208
+ - `kb list <noun>` - List visible full definitions; opt into paged metric summaries for discovery - `--summary --limit --cursor`
205
209
  - `kb tree` - The whole visible semantic layer: group -> semantic model -> assets, metric families collapsed to one card each
206
210
  - `kb search <query>` - Search semantic models, metrics, groups and nested components - `--limit`
207
211
  - `kb entity <name>` - One entity across every visible semantic model that declares it
@@ -211,7 +215,7 @@ _Generated from the CLI by `npm run gen:commands` - do not hand-edit between the
211
215
  - `kb verify <noun> <name>` - Verify a Knowledge Base asset
212
216
  - `kb unverify <noun> <name>` - Unverify a Knowledge Base asset
213
217
 
214
- **query** - Run SQL against a cached data source or a live warehouse (Snowflake / BigQuery). Check truncated before concluding
218
+ **query**
215
219
  - `query <sql>` - Run SQL against a cached data source or a live warehouse (Snowflake / BigQuery). Check truncated before concluding - `--ds --warehouse --connection --limit --override-rules --verbose --adhoc-reason --apply-conditional --skip-conditional --timeout`
216
220
 
217
221
  **metadata** - Warehouse metadata (Snowflake schemas / BigQuery datasets)
@@ -247,7 +251,7 @@ _Generated from the CLI by `npm run gen:commands` - do not hand-edit between the
247
251
  - `dashboard delete <id>` - Delete a custom dashboard (requires --yes) - `--yes`
248
252
 
249
253
  **connector** - Connection management. OAuth and GitHub connections are set up in the Graphit web app.
250
- - `connector list` - List active connections (Snowflake, BigQuery, Slack)
254
+ - `connector list` - List connections (Snowflake, BigQuery, Slack, GitHub/Bitbucket)
251
255
  - `connector add snowflake-keypair` - Add Snowflake via keypair auth - `--account --user --key --name --warehouse --role --database`
252
256
  - `connector add bigquery-serviceaccount` - Add BigQuery via a service-account key (org admin only) - `--key-file --project --dataset --location --name --max-bytes-billed`
253
257
  - `connector test <id>` - Test a connection
@@ -263,7 +267,7 @@ _Generated from the CLI by `npm run gen:commands` - do not hand-edit between the
263
267
  **plugin** - Inspect Graphit assistant plugin status
264
268
  - `plugin status` - Check plugin/package/skill version health - `--json --quiet --skip-network --repair`
265
269
 
266
- **setup** - Install legacy copied Graphit assistant files for Cursor or fallback setups
270
+ **setup**
267
271
  - `setup` - Install legacy copied Graphit assistant files for Cursor or fallback setups - `--editor --project --update --legacy-copy --remove-legacy-copies --dry-run`
268
272
 
269
273
  <!-- COMMANDS:END -->
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "package": "@graphit/cli",
3
- "version": "0.2.348",
3
+ "version": "0.2.351",
4
4
  "source": "cli/package.json"
5
5
  }
@@ -8,7 +8,7 @@ Load when writing a governed query, explaining a refusal, or reporting provenanc
8
8
  |---|---|
9
9
  | `{{ Metric('revenue') }}` | Reusable metric |
10
10
  | `{{ Dimension('order__channel') }}` | Qualified grouping/filter field |
11
- | `{{ Measure('order_total') }}` | Graphit's model-owned measure extension |
11
+ | `{{ Measure('order_total') }}` / `{{ Measure('order__order_total') }}` | Graphit's model-owned measure extension; the bare form resolves only when the name is unique across shared models, the `entity__name` form pins the owning model like a Dimension path |
12
12
 
13
13
  Legacy token grammar is refused. Keep references inside complete executable SQL and canvas `data-graphit-sql`.
14
14
 
@@ -26,6 +26,8 @@ Read `semantic-authoring.md` for model/metric shapes and `metric-families.md` fo
26
26
 
27
27
  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.
28
28
 
29
+ Target grammar, live and in a repository tree alike: `model:`, `entity:` and `group:` names are lowercase snake; `metric:` and `dimension:` names are UPPER (`metric:REVENUE_USD`). In a `.graphit/rules/*.rule.yml` file every target must name something the tree declares, including assets the same sync creates; a bare model name or the model's fully qualified `DATABASE.SCHEMA.TABLE` relation also resolves. `table:` targets are retired - target the semantic model. A rule's dbt-style `groups:` key is not imported; placement comes from `apply_on`.
30
+
29
31
  Constraints keep their five semantics: required predicate, forbidden column, required filter, required aggregation, and value restriction. Use declared semantic identities and typed values.
30
32
 
31
33
  ## Update
@@ -4,12 +4,12 @@ Load before querying, authoring, or building a dashboard.
4
4
 
5
5
  ## Group-first discovery
6
6
 
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.
7
+ 1. Read visible groups and effective status. Present the groups related to the question and agree on the starting group and audience with the user; carry forward a choice they already made.
8
+ 2. Investigate that group in stages. First inventory its relevant models, metrics, dimensions, measures, entities, families and rules; then read the definitions needed to understand what already exists. Use bounded discovery and continuation rather than loading every full definition at once.
9
+ 3. Alongside that focused investigation, run semantic search for the question and related concepts across other accessible groups. A related definition may live elsewhere. Inspect promising matches in full and explain their group and relevance; finding a match does not silently change the agreed data or authoring scope.
10
+ 4. Look for existing dashboards using dashboard listing and metric usage. Inspect the relevant accessible dashboards before proposing a duplicate. KB search does not search dashboards or retained rules; use their own reads.
11
+ 5. After each meaningful research stage, show what you found and recommend the next step. Let the user choose at real forks; do not silently research everything, build the result and leave them behind.
12
+ 6. Before authoring, bring back the existing definitions and dashboards that answer the question, any reusable pieces, and the specific remaining gap. Recommend reuse, extension or new work and agree on that choice with the user before creating anything. Do not repeat a decision or approval they already supplied.
13
13
 
14
14
  The lowercase group name is the semantic label. Use the server-provided uppercase `domain_keys`/status key for data-source `--domain`.
15
15
 
@@ -30,21 +30,21 @@ Entities replace relationship assets. Topics are metadata, not roots. Synonyms a
30
30
 
31
31
  ## Read roles
32
32
 
33
- - `list` inventories a root noun.
33
+ - `list` inventories a root noun. For metric candidates, enable `summary`; set `limit` for the page size and pass `next_cursor` as `cursor` to continue. The CLI flags are `--summary`, `--limit`, and `--cursor`.
34
34
  - `tree` shows the collapsed hierarchy.
35
35
  - `search` covers models, metrics, groups, and nested components; not retained rules.
36
- - `get` reads one exact root.
36
+ - `get` reads one exact root in full. Inspect candidate metrics and their owning models before deciding that inputs are equivalent; summaries cannot establish reuse.
37
37
  - `entity` reads one entity across visible declaring models.
38
38
  - `family` expands/resolves concrete members.
39
39
  - `explore` accepts semantic-model, metric, or group.
40
40
  - `usage` answers dashboard placement and rule impact.
41
41
 
42
- A capped or empty search is not proof of absence.
42
+ A capped or empty search is not proof of absence. A summary page is also incomplete while `truncated` is true: follow `next_cursor` before declaring a gap. Report returned items against `total`; totals describe visible scope. If `migration_incomplete` is true, report an incomplete catalog instead of proposing missing assets. Summary descriptions are excerpts; full definitions remain available through `get`.
43
43
 
44
44
  ## Governed references
45
45
 
46
- Use `{{ Metric('revenue') }}`, `{{ Dimension('order__channel') }}`, and `{{ Measure('order_total') }}` only for Graphit's measure extension. Legacy token grammar is refused.
46
+ Use `{{ Metric('revenue') }}`, `{{ Dimension('order__channel') }}`, and `{{ Measure('order_total') }}` only for Graphit's measure extension. A bare `Measure('name')` must be unique across shared models; write `Measure('entity__name')` to pin the owner. Legacy token grammar is refused.
47
47
 
48
48
  ## Gap decision
49
49
 
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.
50
+ Research first, then recommend. Show which existing definitions or dashboards already cover the request, what can be reused or extended, and what is still missing. Propose authoring only for that agreed gap, with formula, grain, binding, group, rule impact and verification. Ask when a choice is unresolved; do not treat a request to investigate as permission to build.
@@ -6,7 +6,7 @@ Load when investigating semantic reach or presenting KB results.
6
6
 
7
7
  | Need | Read |
8
8
  |---|---|
9
- | Inventory roots | list semantic-model, metric, group, or rule |
9
+ | Inventory roots | list semantic-model, metric, group, or rule; enable `summary` for metric candidates |
10
10
  | Full root definition | get |
11
11
  | Collapsed hierarchy | tree |
12
12
  | Ranked discovery | search |
@@ -15,7 +15,7 @@ Load when investigating semantic reach or presenting KB results.
15
15
  | Semantic neighborhood | explore semantic-model, metric, or group |
16
16
  | Dashboard/rule impact | usage |
17
17
 
18
- `list metric` is flat. Families collapse in tree/search/family views.
18
+ `list metric` is flat. Families collapse in tree/search/family views. Metric summaries are candidates, not full definitions: use `get` for semantics and model/source binding before reuse. Continue with `next_cursor` while `truncated` is true; a partial page does not establish absence. Omitting `summary` preserves the existing full-definition listing.
19
19
 
20
20
  ## Investigations
21
21
 
@@ -58,6 +58,6 @@ Commands write only the result payload to stdout; all decoration (progress, tabl
58
58
 
59
59
  ## Working artifacts
60
60
 
61
- Keep every local file you create in one place: a `./.graphit/` directory in the working dir (distinct from the `~/.graphit/` credential store). Scratch HTML written before `graphit dashboard update-html <id> --file`, output redirected from `graphit dashboard get-html`, exported PNG/PDF, throwaway SQL - all under `.graphit/`, never scattered across the user's repo. `graphit dashboard export` already defaults its output there (no `--output` needed) and drops a self-ignoring `.gitignore`, so the dir is never committed.
61
+ For ordinary dashboard work, keep scratch HTML, exports and throwaway SQL together in `./.graphit/` (distinct from `~/.graphit/` credentials). Exception: in a repository-owned KB workflow that directory is durable, committed source. Load repo-preparation.md and keep scratch exports elsewhere; never make the definition tree self-ignoring.
62
62
 
63
- These are ephemeral. The platform dashboard is the source of truth and the durable artifact; anything local re-materializes on demand (`graphit dashboard get-html <id>` for the HTML, `graphit dashboard export <id> --format png|pdf` for a rendered image). When you finish a piece of work, offer to remove `.graphit/` - nothing of value is lost. Keep it a soft suggestion, not a forced step.
63
+ Dashboard scratch is ephemeral and can be regenerated through the CLI. Offer cleanup only for artifacts known to be scratch. Never offer to remove a repository-owned `.graphit/` tree or an existing directory whose contents you have not classified.
@@ -21,9 +21,11 @@ Run `graphit kb repo show` first.
21
21
  - `manual` with no binding: initialize. Scaffold `.graphit/` per `repo-preparation.md`,
22
22
  run `graphit kb repo verify --path . --allow-dirty` (a `local_only` plan, never
23
23
  applyable; an uncommitted `.graphit/` is refused without the flag), fix findings until
24
- the verdict passes, commit on a branch, open the PR with the user's own tooling. Then tell an org admin to bind (`graphit kb repo bind --connection <id>
25
- --repo <owner/name> --branch main`), link reviewers (`kb repo link-identity`) and mint
26
- the CI tokens (below). Those are admin actions you never run yourself.
24
+ the verdict passes, commit on a branch, open the PR with the user's own tooling. Then tell an org admin to bind (`graphit kb repo bind --repo <owner/name> --branch main`;
25
+ the connection resolves from the repository; on `connection_ambiguous` pass
26
+ `--connection <id>` from `graphit connector list`'s `id` column, never the card's token
27
+ fingerprint), link reviewers (`kb repo link-identity`) and mint the CI tokens
28
+ (below). Those are admin actions you never run yourself.
27
29
 
28
30
  ## A Data Source through a PR
29
31
 
@@ -34,9 +36,10 @@ Run `graphit kb repo show` first.
34
36
  3. Branch, commit, open the PR with the user's tooling and report the PR link. CI verifies
35
37
  the PR head and applies the merged commit; you never merge or apply.
36
38
 
37
- A `.sql` change through a PR rebuilds the source with the same `ds_id`; a deleted file
38
- tombstones it; removing a source something still references refuses at plan time
39
- (`removal_referenced`).
39
+ A `.sql` change through a PR rebuilds the source with the same `ds_id`. A deleted file
40
+ tombstones it from the second apply on; the first apply keeps the source as `noop` with
41
+ the warning `ds_first_apply_retained` naming the file to commit. Removing a source
42
+ something still references refuses at plan time (`removal_referenced`).
40
43
 
41
44
  ## Both sides
42
45
 
@@ -63,11 +66,15 @@ Typed `{code, message}`; read the code, never the prose.
63
66
  | `binding_incomplete`, `local_only`, `config_revision_moved` | bind first; apply reads the provider, not an upload; reload the binding and verify again |
64
67
  | `plan_stale`, `tree_mismatch`, `action_digest_mismatch`, `plan_not_found`, `verdict_failed` | the plan no longer matches the org or the tree: verify again and apply the fresh plan |
65
68
  | `apply_in_progress`, `migration_in_progress` | an apply or a migration holds the lease: wait, never cancel |
66
- | `not_on_base_branch`, `not_descendant_of_last_import`, `pr_head_mismatch`, `pull_request_not_found`, `pull_request_list_unavailable` | wrong commit or PR: use the merged SHA on the bound branch |
67
- | `approvals_unavailable`, `no_head_bound_approvals`, `identity_unlinked`, `identity_unverified`, `member_removed`, `approver_closure_uncovered`, `write_closure_uncovered` | a linked approver holding every written domain must approve the current head; an admin links identities |
69
+ | `not_on_base_branch`, `not_descendant_of_last_import`, `pr_head_mismatch`, `pull_request_not_merged`, `merged_commit_mismatch`, `pr_base_branch_mismatch` | verify the PR head; apply only its merged commit on the bound branch |
70
+ | `pull_request_not_found`, `pull_request_list_unavailable` | no PR resolved for that commit, or the index is still warming: pass `--pr <id>` naming the merged PR; if unavailable, retry later |
71
+ | `approvals_unavailable`, `no_head_bound_approvals`, `approval_head_binding_unprovable`, `identity_unlinked`, `identity_unverified`, `member_removed`, `approver_closure_uncovered`, `write_closure_uncovered` | check native PR approval, reviewer linkage and current Graphit permissions; incomplete or moved provider evidence refuses. Fix the cause, then verify again |
68
72
  | `certificate_not_applyable`, `migration_requires_plan`, `migration_requires_commit`, `migration_mode_invalid`, `approved_sha_missing`, `approved_sha_mismatch` | migration only: the pre-delete `--kind migration` verify is a certificate; after the delete a fresh `--kind migration` verify yields the plan for `apply --plan <id>`; the SHA must match `bind --approved-sha` |
69
73
  | `token_invalid`, `token_revoked`, `token_scope`, `token_repo_mismatch` | CI token: mint a fresh one, use the right scope, mint for this repository |
70
74
 
75
+ Operation status `failed_retryable`: retry once, then report. `refused` or a `fail`
76
+ verdict: never retry unchanged or route around it.
77
+
71
78
  ## Refusals inside the app
72
79
 
73
80
  In a manual org the in-app agent and UI refuse shared writes with "the Knowledge Base is
@@ -77,11 +84,6 @@ its SQL in", "change its refresh contract in" or "remove it by deleting"
77
84
  `datasources/{name}.sql` "and open a pull request". Private sandboxes and operational verbs
78
85
  (refresh, rebuild, pause) stay direct. The fix is the file and a PR, never a workaround.
79
86
 
80
- ## Failures
81
-
82
- Operation status `failed_retryable`: retry once, then report. `refused` or a `fail`
83
- verdict: never retry; report the finding and its next step. Never route around a refusal.
84
-
85
87
  ## Truthful receipts
86
88
 
87
89
  Report drafted, verified (local-only or provider), PR opened, merged and applied as
@@ -90,12 +92,13 @@ quote the operation id, the plan id and the verdict. Queued or timed out is not
90
92
 
91
93
  ## CI in one paragraph
92
94
 
93
- Two jobs, two tokens, one pinned CLI version. The job exports the token as `GRAPHIT_TOKEN`
94
- in its environment, never as a `--token` argument (argv is visible in process listings and
95
- shell traces; `--token` is the interactive form). On the PR, with the `kb:verify` token:
96
- `graphit kb repo verify --sha $HEAD --pr $PR` (verify and status only), a required check on
97
- the bound branch. On merge, with the `kb:apply` token: `graphit kb repo apply --sha
98
- $MERGED_SHA`, which re-verifies the merged commit (a squash changes the SHA) and applies.
99
- Tokens: `graphit kb repo token mint --scope kb:verify|kb:apply` (admin, shown once, `gkb.`
100
- prefix), `token list`, `token revoke <id>`. The token is CI's authority to call; the PR's
101
- head-bound approvers still hold the write closure, and a token never stands in for them.
95
+ Two jobs, two tokens, one pinned CLI version. Export `GRAPHIT_TOKEN` in the job
96
+ environment, never as `--token` in argv or shell traces. The required PR check uses
97
+ `kb:verify`: `graphit kb repo verify --sha $HEAD --pr $PR` (verify/status only).
98
+ After the customer merges, the protected `kb:apply` job runs
99
+ `graphit kb repo apply --sha $MERGED_SHA`. Both check native reviewers' current
100
+ Graphit permissions; the merger is audit context. Apply revalidates the actual
101
+ merged commit, including squash merges. CI always passes `--sha`; flagless
102
+ interactive verify resolves and reports the bound branch head. Admin commands:
103
+ `graphit kb repo token mint --scope kb:verify|kb:apply` (shown once, `gkb.` prefix),
104
+ `token list`, `token revoke <id>`. Tokens authorize calls, never replace reviewers.