@malloy-publisher/skills 0.1.6 → 0.1.7
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/LICENSE +1 -1
- package/README.md +5 -0
- package/dist/index.js +2 -0
- package/package.json +1 -1
- package/skills/malloy/SKILL.md +6 -1
- package/skills/malloy-analysis/SKILL.md +5 -1
- package/skills/malloy-analysis-pitfalls/SKILL.md +6 -1
- package/skills/malloy-analysis-report/SKILL.md +5 -1
- package/skills/malloy-analyze/SKILL.md +5 -1
- package/skills/malloy-charts/SKILL.md +5 -1
- package/skills/malloy-dashboards/SKILL.md +412 -0
- package/skills/malloy-debug/SKILL.md +5 -1
- package/skills/malloy-define/SKILL.md +5 -1
- package/skills/malloy-discover/SKILL.md +5 -1
- package/skills/malloy-document/SKILL.md +6 -2
- package/skills/malloy-getting-started/SKILL.md +5 -1
- package/skills/malloy-gotchas-modeling/SKILL.md +5 -1
- package/skills/malloy-gotchas-queries/SKILL.md +5 -1
- package/skills/malloy-gotchas-rendering/SKILL.md +5 -1
- package/skills/malloy-html-data-app-embedding/SKILL.md +5 -1
- package/skills/malloy-html-data-app-runtime/SKILL.md +9 -5
- package/skills/malloy-html-data-apps/SKILL.md +7 -3
- package/skills/malloy-html-data-apps/reference/lazy-load.md +5 -0
- package/skills/malloy-html-data-apps/reference/verification-harness.md +5 -0
- package/skills/malloy-lookml-review/SKILL.md +5 -1
- package/skills/malloy-lookml-review/reference/_concepts.md +5 -0
- package/skills/malloy-lookml-review/reference/build-derived-tables.md +5 -0
- package/skills/malloy-lookml-review/reference/build-unnest.md +5 -0
- package/skills/malloy-lookml-review/reference/curate-visibility.md +5 -0
- package/skills/malloy-lookml-review/reference/discover.md +5 -0
- package/skills/malloy-lookml-review/reference/document.md +5 -0
- package/skills/malloy-lookml-review/reference/propose-fields.md +5 -0
- package/skills/malloy-lookml-review/reference/review-coverage.md +5 -0
- package/skills/malloy-materialization/SKILL.md +5 -1
- package/skills/malloy-materialization-tuning/SKILL.md +5 -1
- package/skills/malloy-model/SKILL.md +5 -1
- package/skills/malloy-model/reference/access-modifiers.md +5 -0
- package/skills/malloy-model/reference/analysis-to-model.md +5 -0
- package/skills/malloy-model/reference/bridge-tables.md +5 -0
- package/skills/malloy-model/reference/normalized-schemas.md +5 -0
- package/skills/malloy-model/reference/query-sources.md +5 -0
- package/skills/malloy-modeling/SKILL.md +5 -1
- package/skills/malloy-notebook-chat/SKILL.md +6 -1
- package/skills/malloy-notebooks/SKILL.md +5 -1
- package/skills/malloy-patterns/SKILL.md +5 -1
- package/skills/malloy-phrase-detection/SKILL.md +6 -1
- package/skills/malloy-publish/SKILL.md +5 -1
- package/skills/malloy-queries/SKILL.md +5 -1
- package/skills/malloy-review/SKILL.md +5 -1
- package/skills/malloy-review/reference/output-template.md +5 -0
- package/skills/malloy-review/reference/rubric-correctness.md +5 -0
- package/skills/malloy-review/reference/rubric-documentation.md +5 -0
- package/skills/malloy-review/reference/rubric-governance.md +5 -0
- package/skills/malloy-review/reference/rubric-queries.md +5 -0
- package/skills/malloy-review/reference/rubric-rendering.md +5 -0
- package/skills/malloy-review/reference/rubric-structure.md +5 -0
- package/skills/malloy-review/reference/rubric-style.md +5 -0
- package/skills/malloy-review/reference/scope-resolution.md +5 -0
- package/skills/malloy-review/reference/severity-taxonomy.md +5 -0
- package/skills/malloy-scope/SKILL.md +5 -1
package/LICENSE
CHANGED
package/README.md
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
package/skills/malloy/SKILL.md
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy
|
|
3
3
|
description: Index of all Malloy skills. Use when user asks "malloy help", "what malloy skills are available", "how do I use malloy", or needs guidance on which Malloy skill to use.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
---
|
|
6
|
+
<!--
|
|
7
|
+
Copyright (c) Credible Data Inc.
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
-->
|
|
6
10
|
|
|
7
11
|
# Malloy Skills Index
|
|
8
12
|
|
|
@@ -63,6 +67,7 @@ Every skill in this deployment, by what it is for. Start at a driver; it routes
|
|
|
63
67
|
| Skill | Use when... |
|
|
64
68
|
|-------|-------------|
|
|
65
69
|
| `skill:malloy-publish` | Moving a finished model into a served package (local-to-served handoff) |
|
|
70
|
+
| `skill:malloy-dashboards` | Building a dashboard: a tagged `.malloy` file in a package's `dashboards/` directory, with filter controls and drill-through |
|
|
66
71
|
| `skill:malloy-html-data-apps` | Building an in-package HTML data app (a `public/` directory the package serves) |
|
|
67
72
|
| `skill:malloy-html-data-app-runtime` | Writing the JavaScript that drives that app |
|
|
68
73
|
| `skill:malloy-html-data-app-embedding` | Embedding a served page into a host application |
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-analysis
|
|
3
3
|
description: Workflow for answering data questions against Malloy semantic models over MCP - structured discovery with get_context, query construction with execute_query, verification, and answer delivery. Use whenever the user asks a data question, wants a metric, a breakdown, a trend, or a chart over a model.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
---
|
|
6
|
+
<!--
|
|
7
|
+
Copyright (c) Credible Data Inc.
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
-->
|
|
6
10
|
|
|
7
11
|
# Malloy analysis workflow
|
|
8
12
|
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-analysis-pitfalls
|
|
3
3
|
description: Common data analysis pitfalls to watch for during query construction and result interpretation. Reference this checklist when verifying queries and results to catch errors before presenting an answer.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
---
|
|
6
|
+
<!--
|
|
7
|
+
Copyright (c) Credible Data Inc.
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
-->
|
|
10
|
+
|
|
6
11
|
# Data Analysis Pitfalls
|
|
7
12
|
|
|
8
13
|
Watch for these common mistakes throughout the analysis workflow. When you encounter one, fix it before presenting results.
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-analysis-report
|
|
3
3
|
description: Combine validated Malloy queries into a notebook report or dashboard. Use when the user asks to "create a report", "build a dashboard", "combine these into a report", or wants a persistent multi-query artifact.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
---
|
|
6
|
+
<!--
|
|
7
|
+
Copyright (c) Credible Data Inc.
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
-->
|
|
6
10
|
|
|
7
11
|
# Creating Reports
|
|
8
12
|
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-analyze
|
|
3
3
|
description: Explore data for insights and build views/dashboards/notebooks. Use when user asks to "analyze this data", "find insights", "explore for patterns", "what's interesting", "what's driving X", "build a dashboard", "create views", or any analysis task. For EDA exploration, start at Step 1. For building views on an existing model, jump to View Patterns.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
---
|
|
6
|
+
<!--
|
|
7
|
+
Copyright (c) Credible Data Inc.
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
-->
|
|
6
10
|
|
|
7
11
|
# Analysis with Malloy
|
|
8
12
|
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-charts
|
|
3
3
|
description: Chart selection guidance and renderer reference for Malloy views. Use when choosing visualization types, adding chart annotations, user asks "what chart should I use", "how should I visualize this", or when deciding between bar_chart, line_chart, scatter_chart, etc.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
---
|
|
6
|
+
<!--
|
|
7
|
+
Copyright (c) Credible Data Inc.
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
-->
|
|
6
10
|
|
|
7
11
|
# Chart Selection for Malloy
|
|
8
12
|
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: malloy-dashboards
|
|
3
|
+
description: "Build or modify a Malloy Publisher dashboard, a tagged .malloy file in a package's dashboards/ directory, with auto-rendered filter controls, a grid layout, and # drill click-through. Use when the user asks for a dashboard, a filterable operational view, or drill-through between views, and no code is wanted."
|
|
4
|
+
version: 0.1.7
|
|
5
|
+
---
|
|
6
|
+
<!--
|
|
7
|
+
Copyright (c) Credible Data Inc.
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
-->
|
|
10
|
+
|
|
11
|
+
# Publisher Dashboards
|
|
12
|
+
|
|
13
|
+
> A `dashboards/*.malloy` file **is** a dashboard. It imports the model, either declares one query and applies its own filtering or names views that already do, and tags the layout. Publisher discovers it at package load, renders the filter controls from the givens it references, offers `# drill` click-through between pages, and serves it at `/<env>/<pkg>/dashboards/<name>`. No code, no build step.
|
|
14
|
+
|
|
15
|
+
## When this is the right tool
|
|
16
|
+
|
|
17
|
+
| The user wants | Use |
|
|
18
|
+
| ---------------------------------------------------- | ------------------------------------------------ |
|
|
19
|
+
| A recurring, at-a-glance view behind shared filters | this skill (a dashboard) |
|
|
20
|
+
| A narrative, with prose between the numbers | a notebook (`skill:malloy-notebooks`) |
|
|
21
|
+
| Custom design, branding, or interactions beyond tags | an HTML data app (`skill:malloy-html-data-apps`) |
|
|
22
|
+
| The model itself: sources, measures, joins | `skill:malloy-modeling` |
|
|
23
|
+
|
|
24
|
+
Notebooks and dashboards run the same engine, so **interactivity is not the axis**: both get filter
|
|
25
|
+
controls, URL-addressable state, Apply batching, and `# drill`. Pick on the shape of the document.
|
|
26
|
+
Scanned at a glance is a dashboard; read top to bottom is a notebook.
|
|
27
|
+
|
|
28
|
+
## Build sequence
|
|
29
|
+
|
|
30
|
+
1. **READ THE MODEL FIRST.** Get the real source, view, dimension, and given names from the package:
|
|
31
|
+
`malloy_getContext` if you have it, otherwise the REST model endpoint or the `.malloy` files.
|
|
32
|
+
Never guess a name. A guessed field in a query fails the whole package load, not just that one
|
|
33
|
+
dashboard; a guessed tile or suggest source is quieter, and only shows up in the package warnings.
|
|
34
|
+
2. **DECIDE THE FORM** (below): single-query if the page is one filtered result with parts;
|
|
35
|
+
composite if the views already exist and the job is choosing which to show together.
|
|
36
|
+
3. **DECLARE THE GIVENS** the dashboard will filter by, in the model (usually `givens.malloy`), with
|
|
37
|
+
their control tags: see "Filter controls" below for the syntax and what each tag renders as. Skip
|
|
38
|
+
if they already exist, since a given is a model concern and dashboards share them.
|
|
39
|
+
4. **COMPOSE THE FILE** for `dashboards/`, following the template below, but do not save it yet.
|
|
40
|
+
Import every given it filters by, and every source or query any of those givens names in a
|
|
41
|
+
`suggest`. Both are per-file, and getting the suggest wrong does not error: the control still
|
|
42
|
+
looks like a picker but has no options, and says so underneath, "Could not load the options for
|
|
43
|
+
this control". The package warnings name it too.
|
|
44
|
+
5. **COMPILE IT** with `malloy_compile` (or `POST …/models/<path>/compile`), against the source text,
|
|
45
|
+
before you save, at the path the file will have. **Editing one that already exists needs
|
|
46
|
+
`"scope": "file"`**, which compiles your source AS that file; the default appends it instead, so
|
|
47
|
+
every imported name and the query name collide with the saved copy and you get a wall of
|
|
48
|
+
already-defined errors that reads as broken Malloy rather than a wrong scope. **Editing a shared
|
|
49
|
+
include wants `"scope": "package"`**, which recompiles every file as saved: `file` only checks the
|
|
50
|
+
one you are editing, so renaming a source in `_shared.malloy` passes it while breaking every
|
|
51
|
+
composite that imports it. A clean compile is not a working dashboard: some tag mistakes surface
|
|
52
|
+
at step 6, and some only when you look at the page in step 7. (The third scope, `append`, is the
|
|
53
|
+
default and is what a not-yet-saved file gets.)
|
|
54
|
+
6. **SAVE IT, RELOAD, AND READ THE MANIFEST AND THE WARNINGS.** `malloy_reloadPackage`, or
|
|
55
|
+
`GET …/packages/<pkg>?reload=true`. Check the status the reload returns as well as the warnings:
|
|
56
|
+
a 424 means the package did not load and your edit is not live. **The `warnings` key is absent
|
|
57
|
+
when there are none**, so an empty response is the pass, not a sign you are reading the wrong
|
|
58
|
+
field. Then read `GET …/packages/<pkg>/dashboards/<name>`: its `givens` are exactly the controls
|
|
59
|
+
that will render, which catches a given you imported but never referenced before you open the page,
|
|
60
|
+
and its `query` is the name to run in step 7. See "Read the lint" below.
|
|
61
|
+
7. **OPEN IT AND LOOK.** Not optional; see "What 'done' means".
|
|
62
|
+
|
|
63
|
+
## The two forms
|
|
64
|
+
|
|
65
|
+
**Single-query:** one query whose result is the dashboard. Reach for this by default.
|
|
66
|
+
|
|
67
|
+
```malloy
|
|
68
|
+
##! experimental.givens
|
|
69
|
+
import { order_items, products } from '../storefront.malloy'
|
|
70
|
+
import { CATEGORY, MIN_SALE } from '../givens.malloy'
|
|
71
|
+
|
|
72
|
+
#" Revenue and margin at a glance, and where they come from.
|
|
73
|
+
# artifact { title="Business Overview" } dashboard { columns=12 }
|
|
74
|
+
query: overview is order_items -> {
|
|
75
|
+
where: products.category ~ $CATEGORY and sale_price ~ $MIN_SALE
|
|
76
|
+
|
|
77
|
+
aggregate:
|
|
78
|
+
# label="Revenue"
|
|
79
|
+
# currency
|
|
80
|
+
# colspan=3
|
|
81
|
+
total_sales
|
|
82
|
+
# label="Gross margin"
|
|
83
|
+
# currency
|
|
84
|
+
# colspan=3
|
|
85
|
+
total_margin
|
|
86
|
+
# label="Orders"
|
|
87
|
+
# colspan=3
|
|
88
|
+
order_count
|
|
89
|
+
# label="Avg order value"
|
|
90
|
+
# currency
|
|
91
|
+
# colspan=3
|
|
92
|
+
avg_order_value
|
|
93
|
+
|
|
94
|
+
nest:
|
|
95
|
+
# break
|
|
96
|
+
# colspan=6
|
|
97
|
+
# label="Revenue by month"
|
|
98
|
+
sales_by_month
|
|
99
|
+
# colspan=6
|
|
100
|
+
# label="Revenue by state"
|
|
101
|
+
sales_by_state
|
|
102
|
+
nest:
|
|
103
|
+
# colspan=12
|
|
104
|
+
# label="Category performance"
|
|
105
|
+
by_category
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The `#"` line above the tag is a doc comment, and it is the page's description. If you leave `title=`
|
|
110
|
+
off the artifact tag, it becomes the title instead, so write it as one, not as a sentence about the
|
|
111
|
+
page. **It belongs to the query, so it only works on this form.** Putting a `#"` above a
|
|
112
|
+
model-level `##` tag fails the whole package load with "Object annotation not connected to any
|
|
113
|
+
object", and a composite has no description as a result.
|
|
114
|
+
|
|
115
|
+
**Composite:** a list of views that already exist, each run separately into one grid. The tag is
|
|
116
|
+
model-level (`##`) because there is no query of its own to hang a `#` tag on.
|
|
117
|
+
|
|
118
|
+
```malloy
|
|
119
|
+
##! experimental.givens
|
|
120
|
+
## artifact { title="Seasonality" tiles=["scoped_sales -> sales_by_month", "scoped_sales -> sales_by_year", "scoped_sales -> seasonality"] dashboard_columns=3 }
|
|
121
|
+
import { scoped_sales } from './_shared.malloy'
|
|
122
|
+
import { products } from '../storefront.malloy'
|
|
123
|
+
import { CATEGORY, SINCE } from '../givens.malloy'
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
A composite has no query, so the filtering it applies must live in what it composes: a source that
|
|
127
|
+
already has the givens applied. Put it in an untagged `dashboards/_shared.malloy`, which discovery
|
|
128
|
+
treats as a shared include rather than a dashboard. **It has to apply every given the composite
|
|
129
|
+
imports**: a given the composite imports but nothing references gets no control, silently, at reload
|
|
130
|
+
200 with no warning. Note `SINCE` is a `date` rather than a `filter<>`, so it compares with `>=`
|
|
131
|
+
rather than `~`. Save the include before you compile the dashboard that imports it, since an importer
|
|
132
|
+
compiled against a sibling that is not on disk fails with an `import-error`.
|
|
133
|
+
|
|
134
|
+
```malloy
|
|
135
|
+
##! experimental.givens
|
|
136
|
+
import { order_items } from '../storefront.malloy'
|
|
137
|
+
import { CATEGORY, SINCE } from '../givens.malloy'
|
|
138
|
+
|
|
139
|
+
source: scoped_sales is order_items extend {
|
|
140
|
+
where: products.category ~ $CATEGORY and created_at >= $SINCE
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Its tiles are equal-width (there is no per-tile colspan), and each one takes a single column, so
|
|
145
|
+
**`dashboard_columns` is how many tiles you want per row**, not a number of twelfths. Three tiles want
|
|
146
|
+
`dashboard_columns=3`. Setting it to 12 out of habit gives you twelve columns and three tiles a
|
|
147
|
+
twelfth of the page wide. Use the single-query form when one tile deserves more room than the others.
|
|
148
|
+
|
|
149
|
+
**Write `dashboard_columns=N` on a composite and `# dashboard { columns=N }` on a single query.**
|
|
150
|
+
A composite forgives the mix, since both spellings feed the manifest field it
|
|
151
|
+
lays out from, but **a single query tagged `dashboard_columns` silently loses its whole layout**, and
|
|
152
|
+
the manifest reports the count either way: see "Losing the grid". A composite's tiles also keep their
|
|
153
|
+
own field names on their axes and column headers, and the Layout section's remedy, inlining the view,
|
|
154
|
+
is not open to you here: naming existing views is the whole point of the form. `tiles=` on a
|
|
155
|
+
single-query artifact tag is dropped the same way, silently.
|
|
156
|
+
|
|
157
|
+
## Layout: the four tags that make a page line up
|
|
158
|
+
|
|
159
|
+
This section is the **single-query** form only. A composite has no colspans and counts tiles per row,
|
|
160
|
+
above. Cards and tiles share one grid, so copy this recipe, and use the same count on every
|
|
161
|
+
single-query dashboard in the package so they read as one product:
|
|
162
|
+
|
|
163
|
+
1. **`columns=12`** on the `# dashboard` tag. Twelve divides by 2, 3, 4 and 6, so a row is even with
|
|
164
|
+
three cards or four.
|
|
165
|
+
2. **A `# colspan` on every card and tile, summing to 12 per row.** Four cards at 3, three at 4, two
|
|
166
|
+
tiles at 6, a full-width table at 12. Omit them and every item falls to a single column, a twelfth
|
|
167
|
+
of the width, which is too narrow for a line chart to draw in at all.
|
|
168
|
+
3. **`# break` on the first tile after the cards.** Otherwise it flows into the columns left beside
|
|
169
|
+
the cards and the next tile wraps. Not needed per row: once a row sums to 12 the next item wraps
|
|
170
|
+
on its own.
|
|
171
|
+
4. **`# label="…"` on every nest and every aggregate**, including the aggregates inside a table
|
|
172
|
+
nest, whose column headers are field names too. The heading is otherwise the view's or field's
|
|
173
|
+
name, and a wide table full of `total_sales` and `order_item_count` is the most visible thing
|
|
174
|
+
between a rough page and a finished one. A view nested **by name** is the exception: you can label
|
|
175
|
+
the tile, but its own field names still reach the chart axes and the column headers, so inline it
|
|
176
|
+
if you want those labelled too.
|
|
177
|
+
|
|
178
|
+
Then the traps:
|
|
179
|
+
|
|
180
|
+
- **No `# size=fill` on a dashboard tile.** Inside a dashboard it measures against the container the
|
|
181
|
+
whole grid was handed, not the tile, so it yields a chart thousands of pixels tall. Tiles already
|
|
182
|
+
size to their colspan.
|
|
183
|
+
- **A KPI card's label is one line that ellipses** rather than wrapping, so a long label in a narrow
|
|
184
|
+
card is truncated with no other sign. Widen the card or shorten the label.
|
|
185
|
+
- **A ratio needs a number format.** `order_count / customer_count` renders as `10.695` on a card;
|
|
186
|
+
`# number="#,##0.0"` is the precision it actually carries.
|
|
187
|
+
- **A `# shape_map` legend is titled with the measure's field _name_, not its `# label`.** Rename it
|
|
188
|
+
in the view: `aggregate: revenue is total_sales`. Renaming drops the measure's own format tags
|
|
189
|
+
though, so `# currency` becomes plain digits unless you re-tag it at the rename. Prefer `# label=`
|
|
190
|
+
anywhere the legend is not the problem.
|
|
191
|
+
- **A series legend sizes itself from the longer of the series label and its widest value**, then
|
|
192
|
+
truncates both. A 4-character label over 4-digit years clips to `20…`; `# label="Order year"`
|
|
193
|
+
instead of `"Year"` buys the room. A legend showing `…` is this, not a data problem.
|
|
194
|
+
|
|
195
|
+
The last two are upstream renderer behavior, cheap to work around in the model.
|
|
196
|
+
|
|
197
|
+
The same tags govern a `# dashboard` **view** run in a notebook cell, since both surfaces render
|
|
198
|
+
through the same code, so a view laid out this way looks the same in a cell as on a dashboard page.
|
|
199
|
+
Height is the one thing the surface decides: a single-query dashboard renders at its natural height,
|
|
200
|
+
a composite's tiles are each capped, and in a notebook a chart cell is capped and a table cell hugs
|
|
201
|
+
its rows.
|
|
202
|
+
|
|
203
|
+
## The rules that actually bite
|
|
204
|
+
|
|
205
|
+
- **The filename is the dashboard's name:** its URL slug, its listing name, and its `# drill`
|
|
206
|
+
target. The query inside can be called anything, and sometimes must be (a query named `regions`
|
|
207
|
+
collides with an imported `regions` source).
|
|
208
|
+
- **Importing a given is what makes it bindable.** Malloy's given namespace is per-file. A given the
|
|
209
|
+
dashboard file does not import gets no control and cannot be sent to it, even when the `where:`
|
|
210
|
+
that references it lives up an import chain. A composite must import the givens its tiles use.
|
|
211
|
+
- **A suggest's source or query has to resolve in the dashboard file too.** `suggest { source=products … }`
|
|
212
|
+
means the dashboard imports `products`.
|
|
213
|
+
- **A model-level `##` tag must be on one line.** Wrapping one always breaks it, but how you find
|
|
214
|
+
out depends on what follows. If the continuation is not valid Malloy you get a compile error. If it
|
|
215
|
+
happens to be, an `import` say, the file compiles clean, quietly stops being a dashboard and
|
|
216
|
+
becomes a shared include, and only the package warnings tell you. Match on the shape rather than the
|
|
217
|
+
words: the message may say a tag "does not parse", or was "refused" or "dropped rather than parsed",
|
|
218
|
+
and on a file that still built it opens "Annotation" rather than "Tag". See "Losing the grid" below.
|
|
219
|
+
- **In a `# dashboard` view, fields render by role.** A top-level `aggregate:` measure is a KPI card,
|
|
220
|
+
so do not nest a `# big_value` view to get one. Each `nest:` is a tile. Give every KPI a
|
|
221
|
+
`# label=`, or the card is headed `total_sales`.
|
|
222
|
+
- **Only table cells are marked drillable.** See "Drill".
|
|
223
|
+
|
|
224
|
+
`skill:malloy-gotchas-rendering` covers the renderer tags in depth; `skill:malloy-charts` covers
|
|
225
|
+
choosing them.
|
|
226
|
+
|
|
227
|
+
## Filter controls
|
|
228
|
+
|
|
229
|
+
Controls come from the `given:` declarations the query references, and the tags on the declaration
|
|
230
|
+
are the control contract, declared once and identical on every dashboard and in every notebook that
|
|
231
|
+
uses them:
|
|
232
|
+
|
|
233
|
+
```malloy
|
|
234
|
+
##! experimental.givens
|
|
235
|
+
|
|
236
|
+
# label="Category" control=select suggest { source=products dimension=category }
|
|
237
|
+
given: CATEGORY :: filter<string> is f''
|
|
238
|
+
|
|
239
|
+
# label="Brand" control=multiselect suggest { query=brand_suggest dimension=brand }
|
|
240
|
+
given: BRAND :: filter<string> is f''
|
|
241
|
+
|
|
242
|
+
# label="Minimum line total" range_min=0 range_max=250
|
|
243
|
+
given: MIN_SALE :: filter<number> is f''
|
|
244
|
+
|
|
245
|
+
# label="Ordered since"
|
|
246
|
+
given: SINCE :: date is @2023-01-01
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
A sixth tag, `description`, is part of the contract and has two spellings that do different things:
|
|
250
|
+
`# description="…"` publishes to the API but Publisher's own UI does not render it, while
|
|
251
|
+
`#(description="…")` renders as helper text under the control but complains about any multi-word
|
|
252
|
+
value, that the prefix "is not a well-formed route", because a route ends at the first space. That
|
|
253
|
+
complaint is a **compile** diagnostic on a compile that still succeeds, not a package warning, so
|
|
254
|
+
step 6 will not show it. Pick by which reader you care about.
|
|
255
|
+
|
|
256
|
+
`control=select`/`multiselect` with a `suggest` renders a picker filled from the data;
|
|
257
|
+
`range_min`/`range_max` on a `filter<number>` renders a slider; a `date` or `timestamp` renders a
|
|
258
|
+
date picker. Which controls appear is per-dashboard, decided by which givens the query references.
|
|
259
|
+
`skill:malloy-modeling` and `docs/givens.md` cover givens themselves.
|
|
260
|
+
|
|
261
|
+
Two per-dashboard options on the artifact tag:
|
|
262
|
+
|
|
263
|
+
- `autorun=false` batches control changes behind an Apply button. Add it once a page is slow enough
|
|
264
|
+
that a reader notices two round trips.
|
|
265
|
+
- `givens { CATEGORY=f'Outerwear' }` sets starting values, not a redeclaration. A URL parameter wins.
|
|
266
|
+
|
|
267
|
+
A notebook takes both at the file level, as `## autorun=false` and `## givens { CATEGORY=f'Outerwear' }`,
|
|
268
|
+
and behaves identically.
|
|
269
|
+
|
|
270
|
+
## Drill
|
|
271
|
+
|
|
272
|
+
`# drill` goes on a model **dimension**, never on a dashboard:
|
|
273
|
+
|
|
274
|
+
```malloy
|
|
275
|
+
# drill { to=["category", "self"] given=CATEGORY }
|
|
276
|
+
dimension: category is products.category
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
`to=<slug>` navigates to that dashboard with the clicked value written into the named given;
|
|
280
|
+
`to=self` filters in place; two or more destinations pop a menu.
|
|
281
|
+
|
|
282
|
+
Declaring it on the dimension is the point: every result that groups by it becomes clickable, in a
|
|
283
|
+
dashboard tile and in a notebook cell alike. So when a view is meant to be drilled, group by the
|
|
284
|
+
tagged dimension. Declaring `dimension: category is products.category` and grouping by `category`
|
|
285
|
+
gives the identical output field name and the identical numbers, and carries the tag.
|
|
286
|
+
|
|
287
|
+
**Always write `given=`.** Without it the given name is the dimension name **verbatim**, so a
|
|
288
|
+
`dimension: category` seeds a given called `category` rather than a declared `given: CATEGORY`. A
|
|
289
|
+
`to=self` survives that, because a surface folds case when it looks up its own given. A `to=<slug>`
|
|
290
|
+
does not: it navigates, still looks like it worked, and arrives as `?category=…`, which the
|
|
291
|
+
destination drops by exact match, so you land on an unfiltered page. Nothing errors, and the lint
|
|
292
|
+
folds case when it checks, so it stays green too. That silence is specific to a name that folds onto
|
|
293
|
+
a declared given. A `to=self` whose name matches nothing at all is caught loudly and is not offered;
|
|
294
|
+
a `to=<slug>` is not checked either way.
|
|
295
|
+
|
|
296
|
+
A drill only lands somewhere useful if the destination declares a control for the given being
|
|
297
|
+
seeded. **No lint checks that.** It verifies that the target slug is a dashboard in the package, and
|
|
298
|
+
for `to=self` that some model declares the given, and stops there. Nothing reads the destination's
|
|
299
|
+
own givens, so click it and look.
|
|
300
|
+
|
|
301
|
+
Cells in a drillable **table** column show it: pointer cursor, and a blue underline on hover. They are
|
|
302
|
+
in the tab order and carry a button role too, so a keyboard reaches them, focus is styled the way
|
|
303
|
+
hover is, and Enter or Space fires the drill. Chart marks get no such affordance in either Publisher
|
|
304
|
+
or Malloyyo, so a dashboard meant to be drilled wants at least one untagged (table) tile. A destination the
|
|
305
|
+
surface cannot honor is not marked and not offered, which is why a `to=self` reads as plain text in a
|
|
306
|
+
document that declares no control for its given.
|
|
307
|
+
|
|
308
|
+
**One thing quietly switches the marking off, per column: another tile rendering a column with the
|
|
309
|
+
same header.** Put a "revenue by category" chart beside a drillable `category` table, which is the
|
|
310
|
+
obvious thing to build, and that column's cells stop being marked. Marking matches columns by their
|
|
311
|
+
rendered header text, so a name a non-drillable field also shows is dropped rather than risk painting
|
|
312
|
+
a dead link. Only a **non-drillable** column suppresses: a second tile that groups by the same tagged
|
|
313
|
+
dimension is fine, which is the arrangement the paragraph above already recommends. **Other drillable
|
|
314
|
+
columns on the same page keep their marking**, so counting marked cells will not tell you: look at the
|
|
315
|
+
column you care about. The clicks still work, so this is invisible
|
|
316
|
+
unless you hover. Give the two different headings with `# label=`. A transposed table is never marked
|
|
317
|
+
either, for a different reason.
|
|
318
|
+
|
|
319
|
+
## Losing the grid
|
|
320
|
+
|
|
321
|
+
A single-query dashboard can come out with its layout wrong in two visibly different ways, and the
|
|
322
|
+
reload is 200 and the manifest reports the column count you asked for in both.
|
|
323
|
+
|
|
324
|
+
**Not a dashboard at all: one plain nested table**, every `# colspan` and `# break` dropped. Either
|
|
325
|
+
you tagged a single query with `dashboard_columns=N`, which only a composite reads, or a `f'…'`
|
|
326
|
+
filter literal in a `givens { … }`
|
|
327
|
+
block shares a line with `# dashboard`. For the second, put `# dashboard` on its own line: writing it
|
|
328
|
+
first on the line does not help, a plain `'Outerwear'` or a bare date is fine, and the composite form
|
|
329
|
+
is immune because its layout comes from the manifest rather than a re-parse.
|
|
330
|
+
|
|
331
|
+
**A dashboard, but nothing lines up**: you wrote `# colspan` without `columns=N`, so the items flow
|
|
332
|
+
side by side at their natural widths instead of aligning to a grid.
|
|
333
|
+
|
|
334
|
+
To tell them apart, run the dashboard's own query, `{"queryName": "<the manifest's query>"}`, and read
|
|
335
|
+
`renderLogs` on the response. Like `warnings`, the key is absent when there is nothing to say.
|
|
336
|
+
Single-query dashboards have no `tiles` in their manifest, so there is no tile query to run:
|
|
337
|
+
|
|
338
|
+
| render log | what it means |
|
|
339
|
+
|---|---|
|
|
340
|
+
| `Unknown render tag 'colspan'` | the renderer never saw a `# dashboard` tag. It does **not** say which of the two causes; check both |
|
|
341
|
+
| `Ignored # colspan … only applies in columns mode` | it saw the tag but there is no count |
|
|
342
|
+
|
|
343
|
+
Neither reaches the package warnings, so step 6 will not show either. A **wrapped `##` tag** is the
|
|
344
|
+
one failure in this family that does: the file is absent from the listing and the package warnings
|
|
345
|
+
say "Tag does not parse (Unclosed '{')".
|
|
346
|
+
|
|
347
|
+
## Read the lint
|
|
348
|
+
|
|
349
|
+
Package warnings after a reload are the dashboard's test suite. Fix all of them:
|
|
350
|
+
|
|
351
|
+
- `# drill … targets "x", which is not a dashboard in this package`: a dead click.
|
|
352
|
+
- `to=self, but no model in this package declares a given "X"`: the clicked value has nowhere to go.
|
|
353
|
+
- `given "X" suggests options from source "y", which this file does not define`: the dropdown will
|
|
354
|
+
be empty, so import it.
|
|
355
|
+
- `filters by given "X", which this file does not import, so no control is shown for it`: the trap
|
|
356
|
+
under "Importing a given is what makes it bindable", which the lint now names for you, with the file
|
|
357
|
+
to fix.
|
|
358
|
+
- A tile that does not resolve to a real view, or a non-positive `dashboard_columns`.
|
|
359
|
+
|
|
360
|
+
Findings carry a `severity`, but `warn` is the ordinary default and tells you nothing about how bad
|
|
361
|
+
one is. Read the text, not the severity and not the count. One message is worth recognising because
|
|
362
|
+
it changes what the rest of the list means: **"Dashboard lint stopped early, so this list is
|
|
363
|
+
incomplete"**. A dashboard withheld from `explores` also loses its own findings, so a short list for a
|
|
364
|
+
withheld file is not a clean bill of health.
|
|
365
|
+
|
|
366
|
+
**Read the status the reload itself returns, not the listing.** One dashboard that fails to compile
|
|
367
|
+
fails the whole package load, and the reload answers **424** with the compile error. A package that
|
|
368
|
+
was already serving then keeps serving its previous version, so the listing still answers 200 and
|
|
369
|
+
looks perfectly healthy while your edit has silently not taken effect.
|
|
370
|
+
|
|
371
|
+
**If you did not catch the 424, `GET /api/v0/status` still knows.** A package serving an older model
|
|
372
|
+
than its files appears in `loadErrors` with **`stale: true`**, the compile message, and the time it
|
|
373
|
+
failed, and the entry clears on the next reload that compiles. That is the one check that works after
|
|
374
|
+
the fact, so make it the first thing you run when a page will not change. A package that never loaded
|
|
375
|
+
at all appears there too, without `stale`, and is absent from the listing entirely.
|
|
376
|
+
|
|
377
|
+
If the reload is 200 and the others are listed but yours is not, discovery skipped the file instead,
|
|
378
|
+
usually a missing or misspelled `# artifact` tag, which is the same mechanism that deliberately skips
|
|
379
|
+
an untagged shared include. There is a second cause if the package's `publisher.json` carries an
|
|
380
|
+
`explores` list: a dashboard whose file is missing from it is withheld rather than served, and the
|
|
381
|
+
warning says so and names the fix. The list is what matters, not the `queryableSources` setting, which
|
|
382
|
+
is `declared` by default; a package with no `explores` list withholds nothing. Where there is one, a
|
|
383
|
+
`suggest` source has to be queryable as well as resolvable, so it needs to be on the list too.
|
|
384
|
+
|
|
385
|
+
**A clean reload is not proof the tags are right.** The checks above read names and resolve them; the
|
|
386
|
+
separate warning for a tag that does not *parse* is syntax only: it carries no
|
|
387
|
+
position and says nothing about a name that does not resolve. It catches *a* malformed tag; its
|
|
388
|
+
absence is not evidence there are none. That is why the last step is opening the page, not reading
|
|
389
|
+
the warning list.
|
|
390
|
+
|
|
391
|
+
## What "done" means
|
|
392
|
+
|
|
393
|
+
- Every source, view, and field name came from the model you read in step 1.
|
|
394
|
+
- The reload returned **200**, not 424. A 424 means the page you are about to look at is the old one.
|
|
395
|
+
- The package reloads with **zero** dashboard warnings, and your dashboard is in the listing at all.
|
|
396
|
+
- **The page is a dashboard and its items line up.** This is the check the others cannot make for
|
|
397
|
+
you: all of them pass both on a page that has become one plain nested table and on a dashboard whose
|
|
398
|
+
items do not align. See "Losing the grid" for which you have.
|
|
399
|
+
- You opened the page and every tile shows real numbers: not stuck loading, not an error, not an
|
|
400
|
+
empty state you did not intend.
|
|
401
|
+
- Each control renders as the widget you intended (a select shows options; a slider is a slider),
|
|
402
|
+
and changing one changes the numbers.
|
|
403
|
+
- If you added a `# drill`, you clicked it and landed where you meant to, with the given seeded.
|
|
404
|
+
- On a single-query dashboard, every card and tile carries a colspan, each row's colspans sum to
|
|
405
|
+
`columns`, and the rows end flush with each other. On a composite, the tiles fill the row rather
|
|
406
|
+
than leaving a gap. Nothing is clipped, no tile is thousands of pixels tall, and no legend or card
|
|
407
|
+
label ends in `…`.
|
|
408
|
+
|
|
409
|
+
## Reference
|
|
410
|
+
|
|
411
|
+
- `docs/dashboards.md`: the full guide this skill condenses.
|
|
412
|
+
- `docs/givens.md`: the givens the controls are generated from.
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-debug
|
|
3
3
|
description: Fix Malloy compile errors and understand error messages. Use when encountering errors in .malloy files, user says "fix this error", "malloy error", "compile error", "syntax error", or sees 20+ cascading errors.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
---
|
|
6
|
+
<!--
|
|
7
|
+
Copyright (c) Credible Data Inc.
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
-->
|
|
6
10
|
|
|
7
11
|
# Debugging Malloy Errors
|
|
8
12
|
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-define
|
|
3
3
|
description: Propose a source plan and field definitions for a Malloy semantic model. Covers picking which sources to model and at what grain, then proposing the specific renames, dimensions, and measures per source, every proposal backed by querying the data.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
---
|
|
6
|
+
<!--
|
|
7
|
+
Copyright (c) Credible Data Inc.
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
-->
|
|
6
10
|
|
|
7
11
|
# Propose sources and definitions
|
|
8
12
|
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-discover
|
|
3
3
|
description: Silent data discovery for Malloy modeling. Used at Step 1 of the modeling workflow. Scans tables, columns, distributions, and relationships without user interaction. The agent builds an internal picture before presenting anything.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
---
|
|
6
|
+
<!--
|
|
7
|
+
Copyright (c) Credible Data Inc.
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
-->
|
|
6
10
|
|
|
7
11
|
# Data Discovery (Step 1, Silent)
|
|
8
12
|
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-document
|
|
3
|
-
description: Add documentation with #(doc) tags to Malloy models so fields and sources are described in plain language. Use when user asks to "add documentation", "add doc tags", "document the model", or wants fields and sources described for natural-language search and discovery. For declaring parameterizable filters with #(filter), see the malloy-model skill. Filters are a runtime/modeling construct (governance, latency, correctness), not a documentation tag.
|
|
4
|
-
version: 0.1.
|
|
3
|
+
description: 'Add documentation with #(doc) tags to Malloy models so fields and sources are described in plain language. Use when user asks to "add documentation", "add doc tags", "document the model", or wants fields and sources described for natural-language search and discovery. For declaring parameterizable filters with #(filter), see the malloy-model skill. Filters are a runtime/modeling construct (governance, latency, correctness), not a documentation tag.'
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
---
|
|
6
|
+
<!--
|
|
7
|
+
Copyright (c) Credible Data Inc.
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
-->
|
|
6
10
|
|
|
7
11
|
# Documenting a Malloy Model
|
|
8
12
|
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-getting-started
|
|
3
3
|
description: First steps for using a Malloy Publisher deployment through its MCP tools. Use when connecting to Publisher for the first time, when you do not yet know the available environments, packages, or models, or when a user asks what data they can explore. Covers verifying the server, discovering data with malloy_getContext, and running a first grounded query.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
---
|
|
6
|
+
<!--
|
|
7
|
+
Copyright (c) Credible Data Inc.
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
-->
|
|
6
10
|
|
|
7
11
|
# Getting started with Malloy Publisher
|
|
8
12
|
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-gotchas-modeling
|
|
3
3
|
description: Common Malloy modeling mistakes and how to avoid them. Read BEFORE writing source definitions, dimensions, measures, or joins. Covers reserved words, NULL checks, date functions, type casts, field management (extend except/accept/rename vs include public/internal/private), and query-based source gotchas.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
---
|
|
6
|
+
<!--
|
|
7
|
+
Copyright (c) Credible Data Inc.
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
-->
|
|
6
10
|
|
|
7
11
|
# Modeling Gotchas
|
|
8
12
|
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-gotchas-queries
|
|
3
3
|
description: Common Malloy query and view mistakes. Read BEFORE writing views, queries, or notebooks. Covers chart constraints, aggregate filters, joined field aliasing, method syntax, and time truncation vs extraction.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
---
|
|
6
|
+
<!--
|
|
7
|
+
Copyright (c) Credible Data Inc.
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
-->
|
|
6
10
|
|
|
7
11
|
# Query & View Gotchas
|
|
8
12
|
|