dataspring-cli 0.4.2__py3-none-any.whl → 0.4.4__py3-none-any.whl
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.
- cli/_skills/dataspring-author/SKILL.md +236 -19
- cli/_skills/dataspring-consume/SKILL.md +104 -22
- cli/_skills/dataspring-correct/SKILL.md +4 -3
- cli/bundled_manifest.py +1 -1
- cli/contract.py +74 -14
- cli/generated.py +69 -50
- cli/main.py +450 -33
- {dataspring_cli-0.4.2.dist-info → dataspring_cli-0.4.4.dist-info}/METADATA +1 -1
- dataspring_cli-0.4.4.dist-info/RECORD +21 -0
- dataspring_cli-0.4.2.dist-info/RECORD +0 -21
- {dataspring_cli-0.4.2.dist-info → dataspring_cli-0.4.4.dist-info}/WHEEL +0 -0
- {dataspring_cli-0.4.2.dist-info → dataspring_cli-0.4.4.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dataspring-author
|
|
3
|
-
description:
|
|
3
|
+
description: Building and documenting an org's data in DataSpring - the datacore loop (pipelines, connections, dbt models, metrics; pull, push, check, deploy, build, run, backfills), dbt YAML descriptions and the business_context doc. Use when editing a datacore workspace or dbt/models/**.yml, writing metrics, loading or backfilling a pipeline, or any file that feeds DataSpring's semantic layer or org context.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Authoring DataSpring's data-layer context
|
|
@@ -9,6 +9,28 @@ You are editing files that an analytical agent will read at query time.
|
|
|
9
9
|
Lazy descriptions silently degrade every query that touches the
|
|
10
10
|
affected entity. This skill exists so the conventions stay tight.
|
|
11
11
|
|
|
12
|
+
## Before you start: sign in and name the org
|
|
13
|
+
|
|
14
|
+
Every command and connection acts in ONE org, named each time; nothing
|
|
15
|
+
switches it for later. CLI: `dataspring auth login` prints a URL and a code
|
|
16
|
+
at once (run it in the background and hand both to the person), then
|
|
17
|
+
`--org <org>`, `DATASPRING_ORG`, or `{"org": "<org>"}` in a
|
|
18
|
+
`.forspring.json` at the workspace root, which is the natural place:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
echo '{"org": "acme"}' > .forspring.json && dataspring whoami --json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
MCP: one connection per org, `https://dataspring.app/api/mcp/?org=<org>`, or
|
|
25
|
+
`Authorization: Bearer ds_...` for a headless agent (the key's own org).
|
|
26
|
+
|
|
27
|
+
A refusal leads with a reason and ends with its fix; match the reason. The
|
|
28
|
+
ones you meet while building: `role_too_low` (connections, pipelines,
|
|
29
|
+
deploy, build, run, cancel and reset need admin; a member can edit models
|
|
30
|
+
and check), `org_required` (ask which org, never guess), `not_signed_in`
|
|
31
|
+
(log in again). The full table, with the CLI exit codes, is in
|
|
32
|
+
`dataspring-consume`.
|
|
33
|
+
|
|
12
34
|
## 1. The contract
|
|
13
35
|
|
|
14
36
|
The descriptions on metrics, dimensions, and semantic models are
|
|
@@ -206,7 +228,51 @@ version produced each issue. Edit the board and the next issue follows.
|
|
|
206
228
|
and is what stops later history columns moving. Rendering and checking
|
|
207
229
|
prose is the consumer's job and is covered in `dataspring-consume`.
|
|
208
230
|
|
|
209
|
-
## 8.2
|
|
231
|
+
## 8.2 The datacore loop (managed tenants)
|
|
232
|
+
|
|
233
|
+
A managed org's pipelines, connections and dbt project are one workspace of
|
|
234
|
+
files. Edit, `check`, `deploy`; nothing an edit or a check does reaches
|
|
235
|
+
production.
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
dataspring datacore pull ./ws # deployed files, each exactly as pushed
|
|
239
|
+
dataspring datacore push ./ws # changed files only, into a new draft
|
|
240
|
+
dataspring datacore check --validate-only # seconds: parse + semantic validation, no build
|
|
241
|
+
dataspring datacore check # waits; exits 1 unless it passes
|
|
242
|
+
dataspring datacore deploy --then-build # admin; rebuilds changed models too
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
- **A new org** pulls its starting workspace (`datacore.yaml`, empty
|
|
246
|
+
`pipelines/` and `connections/`, and in `models/` a starting dbt project,
|
|
247
|
+
see 8.7).
|
|
248
|
+
- **A check** compiles every pipeline, builds the changed models and their
|
|
249
|
+
tests into `<org>_dev`, and dry-runs their SQL. It runs as an identity
|
|
250
|
+
that can write `<org>_dev` only: a project whose `generate_schema_name`
|
|
251
|
+
puts a model in another dataset is refused, naming the models. A
|
|
252
|
+
workspace with no `models/dbt_project.yml` yet passes with dbt `skipped`,
|
|
253
|
+
so land raw data first and add models after.
|
|
254
|
+
- **The semantic manifest is checked per item**, each finding as `FAIL metric
|
|
255
|
+
<name>: <message>` (`check.dbt.failures` on the run). It is also
|
|
256
|
+
dry-imported as a load would import it, so what passes the check imports.
|
|
257
|
+
A ratio metric's `numerator` and `denominator` are metrics, not measures
|
|
258
|
+
(MetricFlow's definition), as are a derived metric's inputs:
|
|
259
|
+
|
|
260
|
+
```yaml
|
|
261
|
+
- name: gross_margin_pct
|
|
262
|
+
type: ratio
|
|
263
|
+
type_params: {numerator: gross_margin, denominator: revenue} # two metrics
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
- **Validate-only while you edit** (`datacore_run` `check` with `build:
|
|
267
|
+
false`; CLI `datacore check --validate-only`): pipelines compiled,
|
|
268
|
+
connections checked, `dbt parse`, the per-metric semantic validation and
|
|
269
|
+
the import's dry run, and no `dbt build`, so a broken metric or model is
|
|
270
|
+
reported in well under a minute and it never waits behind a check or a
|
|
271
|
+
load. It is never a green check: run a full `check` before `deploy`.
|
|
272
|
+
- **Deploy** needs a green check of that exact draft (any edit makes a new
|
|
273
|
+
draft) and starts a run of each windowed pipeline it changed.
|
|
274
|
+
|
|
275
|
+
## 8.3 Iterating on a dbt model (managed tenants)
|
|
210
276
|
|
|
211
277
|
A model change does not need a load. The loop is `datacore_edit` (kind
|
|
212
278
|
`model`), `datacore_run` `check`, `deploy`, then `build`: a run that loads
|
|
@@ -218,6 +284,156 @@ under `models/`. Do not use `run` to see a model change: it reloads every
|
|
|
218
284
|
resource of the pipeline first. CLI: `dataspring datacore build --select
|
|
219
285
|
"marts.finance+" --wait`.
|
|
220
286
|
|
|
287
|
+
**Look before you write, and verify before you deploy.** `run_sql`
|
|
288
|
+
(`dataspring sql`) runs as the org's read identity, which reads the org's
|
|
289
|
+
own `<org>_raw` (what the loads wrote), `<org>_staging` (the staging views
|
|
290
|
+
over raw), `<org>_core`, `<org>_marts` and `<org>_dev` (what the last
|
|
291
|
+
`check` built), and nothing else; `<org>` is the org id with `-` as `_`.
|
|
292
|
+
|
|
293
|
+
- Before writing a staging model, list the raw columns that actually
|
|
294
|
+
exist: `dataspring datacore schema --table <t>` (or
|
|
295
|
+
`dataspring://datacore/schema`). A field that was null on every row
|
|
296
|
+
loaded has no column at all, so a model written against the source API's
|
|
297
|
+
documentation can fail with `Unrecognized name`; select a typed null for
|
|
298
|
+
it instead. `dataspring datacore freshness` gives row counts and the run
|
|
299
|
+
that wrote each table; `dataspring datacore sample <t>` some rows.
|
|
300
|
+
- After a `check`, the models it built (the changed ones and everything
|
|
301
|
+
downstream) are tables and views in `<org>_dev` for 7 days: compare their
|
|
302
|
+
numbers with the source before `deploy` puts them where dashboards read,
|
|
303
|
+
e.g. `dataspring sql "SELECT * FROM <org>_dev.fct_project LIMIT 20"` or
|
|
304
|
+
`dataspring datacore schema --dataset dev`.
|
|
305
|
+
- A script of several statements runs as one job and answers the last
|
|
306
|
+
one's rows; `CREATE TEMP TABLE` in it is scratch space for an analysis
|
|
307
|
+
that ends with the script.
|
|
308
|
+
|
|
309
|
+
## 8.4 Workspace files keep the customer's text
|
|
310
|
+
|
|
311
|
+
The customer's comments in a pipeline, connection or dbt YAML file are
|
|
312
|
+
its documentation. To replace a connection or pipeline whole, send
|
|
313
|
+
`datacore_edit` `apply` the YAML text as `content`: it is validated like a
|
|
314
|
+
`document` and stored byte for byte, so `dataspring datacore pull` returns
|
|
315
|
+
it as written. A `document` (an object) is stored as a fresh dump with no
|
|
316
|
+
comments; use it only for a file you are creating. `patch`, and a
|
|
317
|
+
`metric_edit` / `semantic_model_edit` update or delete in a shared dbt
|
|
318
|
+
file, replace only the lines of the keys or list entries that changed;
|
|
319
|
+
the rest of the file, mixed indentation included, stays byte for byte.
|
|
320
|
+
When that cannot be done (a flow-style document, say) the answer carries
|
|
321
|
+
`reformatted`: the whole file was rewritten in one indentation style.
|
|
322
|
+
Tell the user the diff is large for that reason. `dataspring datacore
|
|
323
|
+
push` sends each changed file as its text the same way, and skips a file
|
|
324
|
+
whose bytes equal the stored one's.
|
|
325
|
+
|
|
326
|
+
## 8.5 Loads, windows and backfills
|
|
327
|
+
|
|
328
|
+
- **Windows are whole inclusive days**, the run's and every chunk's. When an
|
|
329
|
+
API reads its end date as exclusive (`to=2025-03-31` misses 31 March), the
|
|
330
|
+
resource says so and is sent the day after each window's last day:
|
|
331
|
+
|
|
332
|
+
```yaml
|
|
333
|
+
window: { lookback: 7d, param_start: createdFrom, param_end: createdTo, end: exclusive }
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Test an API's end bound with a one-day window before a backfill: a wrong
|
|
337
|
+
guess silently drops the last day of every chunk.
|
|
338
|
+
- **A window is one request** unless it has a `step`. For a source that caps
|
|
339
|
+
rows per request and does not page (e-regnskab: 20,000 item lines), a long
|
|
340
|
+
re-pull is cut into steps, one request each (`7d`, `2w`, `1M`; calendar
|
|
341
|
+
months, as backfill chunks are cut). `end: exclusive` holds per step, and
|
|
342
|
+
the scheduled window, a manual run and every backfill chunk all step:
|
|
343
|
+
|
|
344
|
+
```yaml
|
|
345
|
+
window: { lookback: 365d, step: 1M, param_start: createdFrom, param_end: createdTo, end: exclusive }
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
A rest resource only, and not a `resolve` child: step its parent.
|
|
349
|
+
- **`resolve` fans out over the parent's own window** (its lookback, or the
|
|
350
|
+
run's window), not the child's. When the child's rows lack the parent's
|
|
351
|
+
key, carry it: `include_from_parent: [hnInvoiceSalesID]` adds the column
|
|
352
|
+
`_<parent resource>_<field>` (`_invoices_hnInvoiceSalesID`) to every child
|
|
353
|
+
row; use it in the child's `primary_key` or rename it in `shape`.
|
|
354
|
+
`param` fills a `{param}` placeholder in the endpoint, or else is sent as
|
|
355
|
+
a query parameter.
|
|
356
|
+
- **An endpoint that answers a list of scalars** (`["budget", "forecast"]`)
|
|
357
|
+
loads one row per element in a column `value`; fan out over it with
|
|
358
|
+
`resolve: { field: value, ... }`.
|
|
359
|
+
- **A backfill** is `dataspring datacore run <pipeline> --start ... --end ...
|
|
360
|
+
--chunk 1M`: one child run per chunk, each started as the previous one
|
|
361
|
+
ends. The chunk is `<n>d`, `<n>w` or `<n>M` (`7d`, `2w`, `1M`, `3M` a
|
|
362
|
+
quarter, `12M` a year; no `Y`). A chunk before the last loads only the
|
|
363
|
+
windowed resources (and those they read rows from) and skips dbt; the last
|
|
364
|
+
chunk loads everything and builds the models once. A failed child fails
|
|
365
|
+
the backfill. One backfill per pipeline at a time.
|
|
366
|
+
- **One request per window.** A rest resource without a `paginator` sends
|
|
367
|
+
one request per window and nothing splits it, so a source that caps an
|
|
368
|
+
answer at N rows needs a `lookback`, and a `--chunk`, under N rows.
|
|
369
|
+
--chunk 1M` (or `3M`, `7d`, `2w`): one child run per chunk, each started as
|
|
370
|
+
the previous one ends. A chunk before the last loads only the windowed
|
|
371
|
+
resources, their `resolve` children (fetched for that chunk's parent
|
|
372
|
+
rows) and those they read rows from, and skips dbt; the last chunk
|
|
373
|
+
loads everything and builds the models once. A failed child fails the
|
|
374
|
+
backfill. One backfill per pipeline at a time.
|
|
375
|
+
- **Stop** a queued or running run, or a whole backfill, with `dataspring
|
|
376
|
+
datacore cancel <run_id>` (admin); the schedule fires again at its next slot.
|
|
377
|
+
- **`reset`** drops a pipeline's raw tables and load state. It is
|
|
378
|
+
irreversible: ask first.
|
|
379
|
+
|
|
380
|
+
## 8.6 Writing a pipeline file
|
|
381
|
+
|
|
382
|
+
Do not read DataSpring's source for the grammar: it is served, cut from the
|
|
383
|
+
files the runner validates against.
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
dataspring datacore reference # the schemas, every connector: secrets, tables
|
|
387
|
+
dataspring datacore reference catalog kanpla # one connector, whole, with a file that uses it
|
|
388
|
+
dataspring datacore reference pipeline # the pipeline JSON schema (also: connection)
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
MCP: `dataspring://datacore/reference[/<part>]` or `read(kind=
|
|
392
|
+
"datacore_reference", id=<part>)`; REST `GET /api/datacore/reference`.
|
|
393
|
+
Check the catalog first: a source with a connector is `connector: <name>`
|
|
394
|
+
plus `config` and `secrets`. Otherwise write an inline `source` (or
|
|
395
|
+
`sources`) and `resources`. The rules that bite:
|
|
396
|
+
|
|
397
|
+
- **Secrets are names**, `t-<org>-<connection>-<field>`, lowercase letters,
|
|
398
|
+
digits and hyphens only (no underscore). Set each with `dataspring secret
|
|
399
|
+
set <name> --stdin` before pushing the file that names it.
|
|
400
|
+
- **Windows** are whole inclusive days, `lookback` before today through
|
|
401
|
+
`lookahead` after, in the pipeline's timezone; declare `end: exclusive`
|
|
402
|
+
for an API whose end date is exclusive.
|
|
403
|
+
- **Loads**: `replace` is a full refresh; `merge` needs `primary_key` (or
|
|
404
|
+
`merge_key`); `window_scope: replace` deletes by the `partition` date, so
|
|
405
|
+
use it only when that is the date the API filters on.
|
|
406
|
+
- **Checks**: `min_rows` is refused on a `cursor` resource; use `freshness`.
|
|
407
|
+
- **A `resolve` child of a windowed parent** needs its own `window`, and the
|
|
408
|
+
parent its window in `params` (`{{ window.start }}`), or backfill chunks
|
|
409
|
+
skip the child.
|
|
410
|
+
|
|
411
|
+
The full guide with complete examples is "Writing a pipeline" in
|
|
412
|
+
`dataspring docs --full`.
|
|
413
|
+
|
|
414
|
+
## 8.7 The managed dbt project
|
|
415
|
+
|
|
416
|
+
The workspace's `models/` is a dbt project (dbt-core 1.12, BigQuery). A new
|
|
417
|
+
org starts with one that already keeps the contract; `dataspring datacore
|
|
418
|
+
pull` shows it. The contract:
|
|
419
|
+
|
|
420
|
+
- `profile: 'dataspring'` (the runner writes the profile; dataset
|
|
421
|
+
`<org>_marts`).
|
|
422
|
+
- Sources at `schema: "{{ env_var('DBT_RAW_SCHEMA') }}"` and `database:
|
|
423
|
+
"{{ env_var('DBT_GCP_PROJECT') }}"`, never a dataset name.
|
|
424
|
+
- `generate_schema_name` returns `DBT_SCHEMA_OVERRIDE` when set (a check:
|
|
425
|
+
`<org>_dev`), else `DBT_SCHEMA_PREFIX` + the layer (`+schema: staging` is
|
|
426
|
+
`<org>_staging`).
|
|
427
|
+
- A `metricflow_time_spine` model with `time_spine:` configured; include it
|
|
428
|
+
in `then.dbt_select`.
|
|
429
|
+
- Semantic models and metrics in YAML anywhere under `models/models/`;
|
|
430
|
+
`metric_edit` creates them in `models/models/semantic/metrics/`.
|
|
431
|
+
- `packages.yml` is fetched by `dbt deps` on every run (`dbt_utils`).
|
|
432
|
+
|
|
433
|
+
A deploy builds nothing: `deploy --then-build`, `datacore build`, or a
|
|
434
|
+
pipeline's `then.dbt_select` does. The full contract is "The managed dbt
|
|
435
|
+
project" in `dataspring docs --full`.
|
|
436
|
+
|
|
221
437
|
## 9. Parameter reference
|
|
222
438
|
|
|
223
439
|
<!-- dispatch-params: semantic_model_edit metric_edit board_edit briefing_edit import_manifest business_context_edit warehouse_edit datacore_edit datacore_run -->
|
|
@@ -227,14 +443,14 @@ _Generated from the dispatch registry (`GET /api/dispatch`); do not edit by hand
|
|
|
227
443
|
|
|
228
444
|
MCP tool `semantic_model_edit`; `POST /api/dispatch/semantic_model_edit`. Edit semantic models in the org manifest. Admin/owner only for writes. The body is `{"action": {"action": "<verb>", ...}}`, one verb per table below.
|
|
229
445
|
|
|
230
|
-
**`create`** —
|
|
446
|
+
**`create`** — Create a semantic model (admin/owner).
|
|
231
447
|
|
|
232
448
|
| Parameter | Type | Required | Description |
|
|
233
449
|
|---|---|---|---|
|
|
234
450
|
| `model_data` | object | yes | Full semantic-model definition (name, measures, dimensions, entities) |
|
|
235
451
|
| `expect_version` | string | no | Managed tenants only: the datacore version (draft snapshot id) this edit was written against, from the previous edit's `version` or dataspring://datacore/files; refused with the current one when stale |
|
|
236
452
|
|
|
237
|
-
**`update`** —
|
|
453
|
+
**`update`** — Change a semantic model (admin/owner). On an imported one a change to how it computes answers a dbt YAML proposal and writes nothing; descriptions write at once.
|
|
238
454
|
|
|
239
455
|
| Parameter | Type | Required | Description |
|
|
240
456
|
|---|---|---|---|
|
|
@@ -243,7 +459,7 @@ MCP tool `semantic_model_edit`; `POST /api/dispatch/semantic_model_edit`. Edit s
|
|
|
243
459
|
| `confirmed` | boolean | no, default `False` | set true only after the user confirmed a redefinition of a NATIVE entity whose impact you showed them; has no effect on imported entities |
|
|
244
460
|
| `expect_version` | string | no | Managed tenants only: the datacore version (draft snapshot id) this edit was written against, from the previous edit's `version` or dataspring://datacore/files; refused with the current one when stale |
|
|
245
461
|
|
|
246
|
-
**`delete`** —
|
|
462
|
+
**`delete`** — Delete a semantic model (admin/owner).
|
|
247
463
|
|
|
248
464
|
| Parameter | Type | Required | Description |
|
|
249
465
|
|---|---|---|---|
|
|
@@ -260,14 +476,14 @@ MCP tool `semantic_model_edit`; `POST /api/dispatch/semantic_model_edit`. Edit s
|
|
|
260
476
|
|
|
261
477
|
MCP tool `metric_edit`; `POST /api/dispatch/metric_edit`. Edit metrics in the org manifest. Admin/owner only for writes. The body is `{"action": {"action": "<verb>", ...}}`, one verb per table below.
|
|
262
478
|
|
|
263
|
-
**`create`** —
|
|
479
|
+
**`create`** — Create a metric, native to DataSpring (admin/owner). A ratio's numerator and denominator, a derived metric's inputs and a cumulative or conversion metric's base are metrics, not measures.
|
|
264
480
|
|
|
265
481
|
| Parameter | Type | Required | Description |
|
|
266
482
|
|---|---|---|---|
|
|
267
483
|
| `metric_data` | object | yes | Metric definition (name, type, type_params) |
|
|
268
484
|
| `expect_version` | string | no | Managed tenants only: the datacore version (draft snapshot id) this edit was written against, from the previous edit's `version` or dataspring://datacore/files; refused with the current one when stale |
|
|
269
485
|
|
|
270
|
-
**`update`** —
|
|
486
|
+
**`update`** — Change a metric (admin/owner). On an imported one a change to how it computes answers a dbt YAML proposal and writes nothing; descriptions write at once.
|
|
271
487
|
|
|
272
488
|
| Parameter | Type | Required | Description |
|
|
273
489
|
|---|---|---|---|
|
|
@@ -276,14 +492,14 @@ MCP tool `metric_edit`; `POST /api/dispatch/metric_edit`. Edit metrics in the or
|
|
|
276
492
|
| `confirmed` | boolean | no, default `False` | set true only after the user confirmed a redefinition of a NATIVE entity whose impact you showed them; has no effect on imported entities |
|
|
277
493
|
| `expect_version` | string | no | Managed tenants only: the datacore version (draft snapshot id) this edit was written against, from the previous edit's `version` or dataspring://datacore/files; refused with the current one when stale |
|
|
278
494
|
|
|
279
|
-
**`delete`** —
|
|
495
|
+
**`delete`** — Delete a metric (admin/owner; irreversible).
|
|
280
496
|
|
|
281
497
|
| Parameter | Type | Required | Description |
|
|
282
498
|
|---|---|---|---|
|
|
283
499
|
| `name` | string | yes | |
|
|
284
500
|
| `expect_version` | string | no | Managed tenants only: the datacore version (draft snapshot id) this edit was written against, from the previous edit's `version` or dataspring://datacore/files; refused with the current one when stale |
|
|
285
501
|
|
|
286
|
-
**`preview`** —
|
|
502
|
+
**`preview`** — Test a metric before saving (any role; writes nothing): sample query results or validation errors, plus the `dbt_yaml` that would be committed for it.
|
|
287
503
|
|
|
288
504
|
| Parameter | Type | Required | Description |
|
|
289
505
|
|---|---|---|---|
|
|
@@ -430,17 +646,17 @@ MCP tool `warehouse_edit`; `POST /api/dispatch/warehouse_edit`. Manage the org's
|
|
|
430
646
|
|
|
431
647
|
MCP tool `datacore_edit`; `POST /api/dispatch/datacore_edit`. Edit the datacore workspace: connections, pipelines and the dbt project of a managed tenant, as files. ``kind`` picks the family: The body is `{"action": {"action": "<verb>", ...}}`, one verb per table below.
|
|
432
648
|
|
|
433
|
-
**`apply`** — Write one workspace file whole (create or replace): a connection or pipeline as a ``document`` (validated against its schema and the catalog
|
|
649
|
+
**`apply`** — Write one workspace file whole (create or replace): a dbt project file as ``content``; a connection or pipeline as ``content`` (its YAML text, stored exactly as sent, comments and layout kept) or as a ``document`` (an object, stored as a fresh dump), validated against its schema and the catalog either way.
|
|
434
650
|
|
|
435
651
|
| Parameter | Type | Required | Description |
|
|
436
652
|
|---|---|---|---|
|
|
437
653
|
| `kind` | one of `connection`, `pipeline`, `model` | yes | connection (admin), pipeline (admin) or model (member) |
|
|
438
654
|
| `name` | string | yes | For kind connection or pipeline: the name, i.e. the file name without .yaml (connections/<name>.yaml, pipelines/<name>.yaml). For kind model: the path under models/, the dbt project (models/models/marts/orders.sql, models/models/staging/schema.yml, models/seeds/x.csv, models/dbt_project.yml); no traversal, .sql .yml .yaml .csv .md .txt .json, never target/ or dbt_packages/ |
|
|
439
655
|
| `document` | object | no | kind connection: the whole document (connection.schema.json: kind, base_url, auth with secret NAMES). kind pipeline: the whole document (pipeline.schema.json: a catalog `connector` with config, secrets, overrides; or a `connection` reference, an inline `source` or named `sources`, with `resources`; schedule, checks, then). Secrets are named, never valued; every name must exist |
|
|
440
|
-
| `content` | string | no | kind model: the
|
|
656
|
+
| `content` | string | no | The whole file as text. kind model: required. kind connection or pipeline: the YAML text, validated like a document and stored byte for byte (comments and layout kept); give content or document, not both |
|
|
441
657
|
| `expect_version` | string | no | The datacore version this edit was written against: the `version` the previous edit returned, or dataspring://datacore/files. An edit against a stale version is refused with the current one. Omit to skip the check. |
|
|
442
658
|
|
|
443
|
-
**`patch`** — Apply a JSON merge patch (RFC 7386: a key set to null is removed) to an existing YAML file: a connection, a pipeline (a schedule, a window, a check), or a YAML file under models/ (not SQL).
|
|
659
|
+
**`patch`** — Apply a JSON merge patch (RFC 7386: a key set to null is removed) to an existing YAML file: a connection, a pipeline (a schedule, a window, a check), or a YAML file under models/ (not SQL). Only the lines of the keys the patch changes are rewritten; every other line, comments and indentation included, stays as written (an answer with ``reformatted`` says the file had to be rewritten whole).
|
|
444
660
|
|
|
445
661
|
| Parameter | Type | Required | Description |
|
|
446
662
|
|---|---|---|---|
|
|
@@ -461,13 +677,14 @@ MCP tool `datacore_edit`; `POST /api/dispatch/datacore_edit`. Edit the datacore
|
|
|
461
677
|
|
|
462
678
|
MCP tool `datacore_run`; `POST /api/dispatch/datacore_run`. The datacore's check-then-deploy loop and its operations. The body is `{"action": {"action": "<verb>", ...}}`, one verb per table below.
|
|
463
679
|
|
|
464
|
-
**`check`** — Check the current draft
|
|
680
|
+
**`check`** — Check the current draft (member): compile every pipeline, build the changed dbt models into <org>_dev with their tests, and validate the semantic manifest per metric. Nothing reaches production.
|
|
465
681
|
|
|
466
682
|
| Parameter | Type | Required | Description |
|
|
467
683
|
|---|---|---|---|
|
|
468
684
|
| `note` | string | no | Optional note, recorded on the check run |
|
|
685
|
+
| `build` | boolean | no, default `True` | false = validate only: compile the pipelines, check the connections, dbt parse, the semantic validation per metric and the manifest import's dry run, with no dbt build (an answer in well under a minute, beside a running check or load). It is NEVER the green check deploy or auto_deploy needs |
|
|
469
686
|
|
|
470
|
-
**`deploy`** —
|
|
687
|
+
**`deploy`** — Promote the checked draft to deployed (admin), or roll back to an earlier deployed snapshot; then_build also rebuilds changed models.
|
|
471
688
|
|
|
472
689
|
| Parameter | Type | Required | Description |
|
|
473
690
|
|---|---|---|---|
|
|
@@ -476,14 +693,14 @@ MCP tool `datacore_run`; `POST /api/dispatch/datacore_run`. The datacore's check
|
|
|
476
693
|
| `then_build` | boolean | no, default `False` | When the deploy changed any file under models/, also queue a models-only build (the `build` action) of the newly deployed snapshot on the raw tables already loaded; build_select narrows it. Without it, a deploy that changed models answers with the command that does it |
|
|
477
694
|
| `build_select` | string | no | With then_build: the dbt selector to rebuild (default: every model, `fqn:*`) |
|
|
478
695
|
|
|
479
|
-
**`run`** —
|
|
696
|
+
**`run`** — Load a pipeline now (admin): its own window, a given window, or a chunked backfill whose chunks run back to back.
|
|
480
697
|
|
|
481
698
|
| Parameter | Type | Required | Description |
|
|
482
699
|
|---|---|---|---|
|
|
483
700
|
| `pipeline` | string | yes | Pipeline name (a deployed pipelines/<name>.yaml) |
|
|
484
|
-
| `window` | RunWindowParams | no | A date window to load instead of the pipeline's own; wider than backfill_chunk becomes a chunked backfill. Both ends are inclusive days, and so is every chunk; a resource whose API reads its end bound as exclusive declares `window.end: exclusive` in the pipeline and is sent the day after each chunk's last day |
|
|
701
|
+
| `window` | RunWindowParams | no | A date window to load instead of the pipeline's own; wider than backfill_chunk becomes a chunked backfill. Both ends are inclusive days, and so is every chunk; a resource whose API reads its end bound as exclusive declares `window.end: exclusive` in the pipeline and is sent the day after each chunk's last day. A rest resource whose window has a `step` sends one request per step of the window or chunk, not one for the whole window |
|
|
485
702
|
| `cursor` | RunCursorParams | no | {from: <value>}: a cursor to re-pull from |
|
|
486
|
-
| `backfill_chunk` | string | no | Chunk size for a windowed run:
|
|
703
|
+
| `backfill_chunk` | string | no | Chunk size for a windowed run: <n>d, <n>w or <n>M (days, weeks or calendar months; 1M is the default, 3M a quarter, 12M a year; there is no Y). Every chunk before the last loads only the windowed resources (with their resolve children and the ones they read rows from) and skips its dbt step, each skip recorded with its reason on the child run; the last chunk loads every resource and builds the models once |
|
|
487
704
|
|
|
488
705
|
**`build`** — Rebuild the deployed dbt models on the raw tables already loaded: no resource is loaded.
|
|
489
706
|
|
|
@@ -492,14 +709,14 @@ MCP tool `datacore_run`; `POST /api/dispatch/datacore_run`. The datacore's check
|
|
|
492
709
|
| `pipeline` | string | no | A deployed pipeline whose `then` block names what to build (its dbt_select, dbt_exclude and tests). Omit to build every model of the deployed project |
|
|
493
710
|
| `select` | string | no | A dbt selector (`dbt build --select`), e.g. `marts.finance+` or `+fct_postings`; replaces the pipeline's dbt_select and dbt_exclude. Default: the pipeline's then.dbt_select, or every model (`fqn:*`) when no pipeline is named |
|
|
494
711
|
|
|
495
|
-
**`reset`** —
|
|
712
|
+
**`reset`** — Delete a pipeline's dlt state and drop its raw tables (admin; irreversible).
|
|
496
713
|
|
|
497
714
|
| Parameter | Type | Required | Description |
|
|
498
715
|
|---|---|---|---|
|
|
499
716
|
| `pipeline` | string | yes | The pipeline whose dlt state and raw tables to remove |
|
|
500
717
|
| `approve` | boolean | no, default `False` | Must be true: the reset is irreversible. Ask the user first |
|
|
501
718
|
|
|
502
|
-
**`cancel`** —
|
|
719
|
+
**`cancel`** — Stop a queued or running run, or a whole backfill (admin); the schedule fires again at its next slot.
|
|
503
720
|
|
|
504
721
|
| Parameter | Type | Required | Description |
|
|
505
722
|
|---|---|---|---|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dataspring-consume
|
|
3
|
-
description: How to query DataSpring correctly to answer analytical questions. Use when the user is asking analytical/business questions, when an agent is connected to a DataSpring MCP server, when running `dataspring query ...` from the terminal, or when scripting against DataSpring's REST API. Covers the three surfaces (MCP, CLI, REST) — same principles, different invocations.
|
|
3
|
+
description: How to query DataSpring correctly to answer analytical questions. Use when the user is asking analytical/business questions, when an agent is connected to a DataSpring MCP server, when running `dataspring query ...` from the terminal, or when scripting against DataSpring's REST API. Covers the three surfaces (MCP, CLI, REST) — same principles, different invocations — and signing in, naming the org, and what to do with a refusal.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Querying DataSpring to answer analytical questions
|
|
@@ -24,10 +24,10 @@ traces — departing from them produces misleading numbers.
|
|
|
24
24
|
| List dimensions | `list_dimensions()` | `dataspring dimensions list` | `GET /api/semantic-layer/dimensions` |
|
|
25
25
|
| Explain one metric | `explain_metric(metric_name)` | `dataspring metrics show <name>` | `GET /api/semantic-layer/metrics/<name>` |
|
|
26
26
|
| Query | `query_metrics(metrics, dimensions?, grain?, start_date?, end_date?, where?, ...)` | `dataspring query -m <name> [-d ...] [-g month] ...` | `POST /api/dispatch/query_metrics` with the same fields |
|
|
27
|
-
| Explain what was computed | `explain_query(metrics, ...)` (same fields as `query_metrics`) |
|
|
27
|
+
| Explain what was computed | `explain_query(metrics, ...)` (same fields as `query_metrics`) | `dataspring explain_query --metrics <name> ...` | `POST /api/dispatch/explain_query` |
|
|
28
28
|
| What DataSpring learned / undo | `learned_edit list` / `undo` | `dataspring learned list` | `GET /api/learned` |
|
|
29
|
-
| Verified queries | `verified_query_edit list` / `record` |
|
|
30
|
-
| Read business_context | `
|
|
29
|
+
| Verified queries | `verified_query_edit list` / `record` | `dataspring verified_query_edit list` | `POST /api/dispatch/verified_query_edit` |
|
|
30
|
+
| Read business_context | `business_context_edit get` (also in the connection's instructions) | `dataspring business-context get` | `GET /api/context` |
|
|
31
31
|
| Check business_context size | `business_context_edit show_size` | `dataspring business-context size` | (no dedicated route — use `GET /api/context` and measure) |
|
|
32
32
|
| Org's published skill | (this skill, served via SKILL.md) | `dataspring skill instructions consume` | `GET /api/v1/skill/consume/instructions` |
|
|
33
33
|
| Be woken when a number moves | `watch_edit apply` | `dataspring watch_edit apply --document-json @watch.yaml` | `POST /api/dispatch/watch_edit` |
|
|
@@ -51,15 +51,44 @@ reads is the one this deployment was built with:
|
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
53
|
curl -fsSL https://dataspring.app/install.sh | sh # Windows: irm https://dataspring.app/install.ps1 | iex
|
|
54
|
-
dataspring login
|
|
54
|
+
dataspring auth login
|
|
55
55
|
dataspring skills install --all # into ~/.claude/skills/<name>/, stamped
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
Run `dataspring skills install` again after `dataspring upgrade` (the CLI
|
|
59
|
-
upgrades itself in place
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
`skills`
|
|
59
|
+
upgrades itself in place through uv tool, pipx or pip, and prints the
|
|
60
|
+
commands to run when it cannot tell which installed it; `dataspring
|
|
61
|
+
--version` says when the server has operations this CLI does not know).
|
|
62
|
+
`dataspring skills diff` shows what drifted between the installed copy and
|
|
63
|
+
the served one; `--offline` on any `skills` command uses the copy bundled in
|
|
64
|
+
the CLI instead of the server.
|
|
65
|
+
|
|
66
|
+
## Signing in and naming the org
|
|
67
|
+
|
|
68
|
+
People sign in with their Forspring account; who is in which org, with
|
|
69
|
+
which role, is the account's answer on every request. DataSpring has no
|
|
70
|
+
"current org": every command and every connection names one.
|
|
71
|
+
|
|
72
|
+
- **CLI.** `dataspring auth login` prints a URL and a code at once and waits
|
|
73
|
+
until a person approves it in a browser (the machine running the CLI needs
|
|
74
|
+
none). Run it in the background, hand the person the URL and the code, and
|
|
75
|
+
wait for it to exit. It stores a `ds_` key for the org they approve
|
|
76
|
+
(`--all-orgs`: one per org). With one key stored, commands act in that org.
|
|
77
|
+
With several, name the org on every command, in this order of precedence:
|
|
78
|
+
`--org <org>`, `DATASPRING_ORG` (then `FORSPRING_ORG`), or a
|
|
79
|
+
`.forspring.json` holding `{"org": "<org>"}` in the working directory or a
|
|
80
|
+
parent. `DATASPRING_API_KEY=ds_...` (CI) acts in the key's own org.
|
|
81
|
+
`dataspring whoami --json` says which org a command here acts in and why
|
|
82
|
+
(`org_from`). There is no command that switches org for later commands.
|
|
83
|
+
- **MCP.** One connection per org: `https://dataspring.app/api/mcp/?org=<org>`
|
|
84
|
+
(the slash before `?` is required), signed in with the Forspring account.
|
|
85
|
+
To work in two orgs, add two connections; no tool moves a connection. A
|
|
86
|
+
headless agent connects to the same URL with `Authorization: Bearer ds_...`;
|
|
87
|
+
the key acts in its own org, and `?org=` may only repeat it.
|
|
88
|
+
- **REST.** `Authorization: Bearer ds_...`; the key is the org.
|
|
89
|
+
|
|
90
|
+
Never pick an org for the person. When the org is ambiguous you are refused
|
|
91
|
+
with `org_required` and the list of their orgs (§11): ask which one.
|
|
63
92
|
|
|
64
93
|
## 1. Knowledge layers — consult before querying
|
|
65
94
|
|
|
@@ -271,6 +300,31 @@ two queries (before/after) and calling out the methodology change rather
|
|
|
271
300
|
than silently quoting one misleading trend. Same rule regardless of
|
|
272
301
|
surface — same warehouse, same boundaries.
|
|
273
302
|
|
|
303
|
+
## 6.1 Say when a period is partial
|
|
304
|
+
|
|
305
|
+
A grouped query is clamped to the window you asked for, so its first and
|
|
306
|
+
last buckets can hold only part of their period. The answer then carries
|
|
307
|
+
`effective_window`: `covered` (the days the numbers cover, always the window
|
|
308
|
+
asked for), `partial_periods` (each cut-short bucket with the days it holds)
|
|
309
|
+
and a `note`. It is absent when every bucket is whole. Quarter to date at
|
|
310
|
+
quarter grain, or January to September at year grain, comes back as ONE
|
|
311
|
+
row labelled with the whole period:
|
|
312
|
+
|
|
313
|
+
```json
|
|
314
|
+
"effective_window": {"covered": {"start": "2025-01-01", "end": "2025-09-30"},
|
|
315
|
+
"partial_periods": [{"bucket": "2025-01-01", "start": "2025-01-01", "end": "2025-09-30"}]}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Label every partial bucket with the days it holds, beside its number, and
|
|
319
|
+
never compare it with a whole period as if it were one:
|
|
320
|
+
|
|
321
|
+
> 2025 to date (1 January to 30 September): 4.2M DKK, against 3.9M DKK for
|
|
322
|
+
> the same nine months of 2024.
|
|
323
|
+
|
|
324
|
+
For a like-for-like comparison send `compare_to: same_period_last_year` (or
|
|
325
|
+
`previous_period`), which compares the same days. `explain_query` reports the
|
|
326
|
+
same `effective_window` before you run anything.
|
|
327
|
+
|
|
274
328
|
## 7. Insights, not just data
|
|
275
329
|
|
|
276
330
|
Don't just return raw query results. Interpret what the number
|
|
@@ -488,8 +542,8 @@ Examples of honest refusals:
|
|
|
488
542
|
|
|
489
543
|
## 11. Tool / response error recovery
|
|
490
544
|
|
|
491
|
-
|
|
492
|
-
|
|
545
|
+
A failed query comes back with an `error`, a `code` and a `suggestion`
|
|
546
|
+
(`hint` is an alias older clients read). Follow the suggestion.
|
|
493
547
|
|
|
494
548
|
Codes you'll see:
|
|
495
549
|
|
|
@@ -523,10 +577,34 @@ replaced the sentence the reader actually needed. If you could not
|
|
|
523
577
|
recover, say what you could not find out and what you would need — that
|
|
524
578
|
is about THEM. If you did recover, just answer.
|
|
525
579
|
|
|
526
|
-
Surface shape: **MCP** returns `{error, code, suggestion}`
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
`{"detail":
|
|
580
|
+
Surface shape: **MCP** returns `{error, code, suggestion}` as the tool
|
|
581
|
+
result; **REST** (`POST /api/dispatch/<tool>`) answers the same body with
|
|
582
|
+
HTTP 200, and a body the tool's schema refuses (or an operation that fails
|
|
583
|
+
outright) with 400 `{"detail": ...}`; **CLI** prints `✗ Error: …` with a `Hint:` line and exits 1.
|
|
584
|
+
|
|
585
|
+
### Refusals: who you are, which org, what your role allows
|
|
586
|
+
|
|
587
|
+
A refusal is not a query error. It has a stable `reason`, a `note` saying
|
|
588
|
+
what happened and a `fix` saying what fixes it. **REST** answers it with the
|
|
589
|
+
reason's HTTP status and `{"ok": false, "reason", "note", "fix"}` (plus
|
|
590
|
+
`org`, and `contacts` and `fix_url` when the account named who can fix it);
|
|
591
|
+
**MCP** raises a tool error whose text is `<reason>: <note> Fix: <fix>`;
|
|
592
|
+
the **CLI** prints the same line and exits with the reason's code. Match on
|
|
593
|
+
the reason, never on the prose, and pass the fix on verbatim.
|
|
594
|
+
|
|
595
|
+
| Reason | CLI exit | What you do |
|
|
596
|
+
|---|---|---|
|
|
597
|
+
| `not_signed_in` | 4 | CLI: run `dataspring auth login` and hand the person its URL and code. MCP: reconnect and sign in. Never ask for a password or a key in chat. |
|
|
598
|
+
| `org_required` | 2 | Several orgs, none named. Ask the person which one (the refusal lists them); then `--org`, `DATASPRING_ORG` or `.forspring.json`. Never guess. |
|
|
599
|
+
| `not_a_member` | 3 | Check the org id's spelling against the list. Otherwise only an owner or admin of that org can add the person, at the Forspring account; say so and stop. |
|
|
600
|
+
| `product_off` | 4 | DataSpring is not switched on for this person in that org. Tell them who can switch it on (`contacts`, `fix_url`); do not retry. |
|
|
601
|
+
| `org_suspended` | 4 | Billing stopped DataSpring for the whole org. Tell them an owner can restart it on the billing page; do not retry. |
|
|
602
|
+
| `role_too_low` | 4 | Their role cannot do this. Offer what it can do (a personal preference or quick metric instead of an org-wide change), or name the role needed. |
|
|
603
|
+
| `org_mismatch` | 4 | The key or connection is for another org than the one named. Use that org's key or URL, or drop the conflicting `--org` / `?org=`. |
|
|
604
|
+
| `account_unavailable` | 1 | Forspring's account did not answer, so access could not be checked. Retry once after a few seconds; nothing changed. |
|
|
605
|
+
|
|
606
|
+
Other CLI exits: 0 success, 1 any other failure, 2 a usage error, 64 a
|
|
607
|
+
removed command (it prints what replaced it).
|
|
530
608
|
|
|
531
609
|
## 12. Multi-turn / multi-call efficiency
|
|
532
610
|
|
|
@@ -606,9 +684,13 @@ How it reaches you:
|
|
|
606
684
|
`dataspring business-context size` shows the byte budget.
|
|
607
685
|
- **REST**: `GET /api/context` returns `{"content": "..."}`.
|
|
608
686
|
|
|
687
|
+
- **MCP**: the connection's instructions carry it from the first turn;
|
|
688
|
+
`business_context_edit` `get` reads it again. (`dataspring://context` is
|
|
689
|
+
the user's own query preferences, not this document.)
|
|
690
|
+
|
|
609
691
|
Treat `business_context` as part of the same authoritative layer
|
|
610
|
-
as metric descriptions.
|
|
611
|
-
|
|
692
|
+
as metric descriptions: the same trust level. It is edited in DataSpring
|
|
693
|
+
(`business_context_edit`), and an edit is live at once.
|
|
612
694
|
|
|
613
695
|
That's the working agreement. Same principles, three surfaces —
|
|
614
696
|
do this and the answers will be right.
|
|
@@ -699,11 +781,11 @@ MCP tool `explain_query`; `POST /api/dispatch/explain_query`. Show what a query
|
|
|
699
781
|
|
|
700
782
|
#### `run_sql`
|
|
701
783
|
|
|
702
|
-
MCP tool `run_sql`; `POST /api/dispatch/run_sql`. Run
|
|
784
|
+
MCP tool `run_sql`; `POST /api/dispatch/run_sql`. Run read-only SQL (one statement, or a script whose last statement answers) against the org's warehouse and get the rows back, ungoverned.
|
|
703
785
|
|
|
704
786
|
| Parameter | Type | Required | Description |
|
|
705
787
|
|---|---|---|---|
|
|
706
|
-
| `sql` | string | yes | One SQL statement (BigQuery Standard SQL on a managed warehouse). Name tables as <org>_marts.<table>, <org>_core.<table> or <org>
|
|
788
|
+
| `sql` | string | yes | One SQL statement (BigQuery Standard SQL on a managed warehouse), or a script of several separated by ';' whose last statement's rows are returned (CREATE TEMP TABLE ... ; SELECT ... works: temp tables are the scratch space, gone when the script ends). Name tables as <org>_marts.<table>, <org>_core.<table>, <org>_staging.<table>, <org>_raw.<table> (what the loads wrote) or <org>_dev.<table> (what the last datacore check built; expire after 7 days), <org> being the organization id with every hyphen as an underscore (acme-dk is acme_dk_marts); the identity it runs as can read those five datasets of its own org and nothing else, so a write to a dataset or a reach outside them is refused by the warehouse. |
|
|
707
789
|
| `warehouse` | string | no | One of the org's warehouses (see dataspring://warehouses); omit for the active one. |
|
|
708
790
|
| `max_rows` | integer | no, default `1000` | Rows to return at most (default 1000, cap 10000). Aggregate in SQL rather than paging. |
|
|
709
791
|
| `format` | one of `json`, `csv`, `markdown` | no, default `json` | 'json' answers rows in `data`; 'csv' or 'markdown' answer one document in `content`. |
|
|
@@ -766,7 +848,7 @@ MCP tool `update_context`; `POST /api/dispatch/update_context`. Update user pref
|
|
|
766
848
|
|
|
767
849
|
MCP tool `quick_metric_edit`; `POST /api/dispatch/quick_metric_edit`. Edit user-defined quick metrics (arithmetic expressions over existing metrics). The body is `{"action": {"action": "<verb>", ...}}`, one verb per table below.
|
|
768
850
|
|
|
769
|
-
**`create`** —
|
|
851
|
+
**`create`** — Save a personal quick metric: arithmetic over existing metrics, queried as `qm:<name>`.
|
|
770
852
|
|
|
771
853
|
| Parameter | Type | Required | Description |
|
|
772
854
|
|---|---|---|---|
|
|
@@ -774,7 +856,7 @@ MCP tool `quick_metric_edit`; `POST /api/dispatch/quick_metric_edit`. Edit user-
|
|
|
774
856
|
| `expression` | string | yes | Arithmetic expression over existing metrics (e.g., "total_revenue / order_count") |
|
|
775
857
|
| `description` | string | no | |
|
|
776
858
|
|
|
777
|
-
**`update`** —
|
|
859
|
+
**`update`** — Change one of your quick metrics.
|
|
778
860
|
|
|
779
861
|
| Parameter | Type | Required | Description |
|
|
780
862
|
|---|---|---|---|
|
|
@@ -783,7 +865,7 @@ MCP tool `quick_metric_edit`; `POST /api/dispatch/quick_metric_edit`. Edit user-
|
|
|
783
865
|
| `expression` | string | no | |
|
|
784
866
|
| `description` | string | no | |
|
|
785
867
|
|
|
786
|
-
**`delete`** —
|
|
868
|
+
**`delete`** — Delete one of your quick metrics.
|
|
787
869
|
|
|
788
870
|
| Parameter | Type | Required | Description |
|
|
789
871
|
|---|---|---|---|
|
|
@@ -12,8 +12,9 @@ readers see it, and be exact about what changed.
|
|
|
12
12
|
|
|
13
13
|
Before agreeing anything is wrong, call `explain_query`; with no
|
|
14
14
|
`params` it explains this session's last query. Quote back the metric it
|
|
15
|
-
resolved, the filters
|
|
16
|
-
|
|
15
|
+
resolved, the filters (standing preferences included), the window, and
|
|
16
|
+
briefly the SQL. A bucket in `effective_window.partial_periods` (a year to
|
|
17
|
+
date labelled as the year) is often the whole dispute: name its days. Often the number is right and the disagreement
|
|
17
18
|
is about which metric was meant — ambiguity, not a correction. Never
|
|
18
19
|
apologise for a number you have not re-read.
|
|
19
20
|
|
|
@@ -48,7 +49,7 @@ Read the WHAT YOU MAY CHANGE block — this user's targets, nothing else. An
|
|
|
48
49
|
admin gets the systemic fix; a member or viewer a personal preference,
|
|
49
50
|
verified query or quick metric, which fixes their experience immediately.
|
|
50
51
|
Both are complete answers: never route a user elsewhere, never offer what
|
|
51
|
-
ends in a
|
|
52
|
+
ends in a `role_too_low` refusal. §7 is the same split for a coverage gap.
|
|
52
53
|
|
|
53
54
|
## 4. Ownership and ceremony
|
|
54
55
|
|
cli/bundled_manifest.py
CHANGED
|
@@ -6,7 +6,7 @@ the server's to say whether the server has operations this CLI does not
|
|
|
6
6
|
know. tests/test_skills_surface.py asserts it is current.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
MANIFEST_HASH = "
|
|
9
|
+
MANIFEST_HASH = "54af1f0a9d07ef692b63e0648d835ce5efc020bd9588db6d75897b36af4e0232"
|
|
10
10
|
TOOL_KEYS = (
|
|
11
11
|
"business_context_edit",
|
|
12
12
|
"semantic_model_edit",
|