@malloy-publisher/skills 0.1.9 → 0.1.10
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/package.json +1 -1
- package/skills/malloy/SKILL.md +3 -2
- package/skills/malloy-analysis/SKILL.md +1 -1
- package/skills/malloy-analysis-pitfalls/SKILL.md +1 -1
- package/skills/malloy-analysis-report/SKILL.md +2 -2
- package/skills/malloy-analyze/SKILL.md +5 -3
- package/skills/malloy-charts/SKILL.md +1 -1
- package/skills/malloy-dashboards/SKILL.md +123 -59
- package/skills/malloy-debug/SKILL.md +1 -1
- package/skills/malloy-define/SKILL.md +2 -2
- package/skills/malloy-discover/SKILL.md +1 -1
- package/skills/malloy-document/SKILL.md +6 -4
- package/skills/malloy-getting-started/SKILL.md +1 -1
- package/skills/malloy-gotchas-modeling/SKILL.md +1 -1
- package/skills/malloy-gotchas-queries/SKILL.md +1 -1
- package/skills/malloy-gotchas-rendering/SKILL.md +1 -1
- package/skills/malloy-html-data-app-embedding/SKILL.md +1 -1
- package/skills/malloy-html-data-app-runtime/SKILL.md +1 -1
- package/skills/malloy-html-data-apps/SKILL.md +1 -1
- package/skills/malloy-lookml-review/SKILL.md +1 -1
- package/skills/malloy-materialization/SKILL.md +1 -1
- package/skills/malloy-materialization-tuning/SKILL.md +1 -1
- package/skills/malloy-model/SKILL.md +14 -2
- package/skills/malloy-model-as-you-go/SKILL.md +306 -0
- package/skills/malloy-modeling/SKILL.md +5 -6
- package/skills/malloy-notebook-chat/SKILL.md +1 -1
- package/skills/malloy-notebooks/SKILL.md +1 -1
- package/skills/malloy-patterns/SKILL.md +1 -1
- package/skills/malloy-phrase-detection/SKILL.md +1 -1
- package/skills/malloy-publish/SKILL.md +1 -1
- package/skills/malloy-queries/SKILL.md +1 -1
- package/skills/malloy-review/SKILL.md +1 -1
- package/skills/malloy-scope/SKILL.md +1 -1
package/package.json
CHANGED
package/skills/malloy/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -42,7 +42,8 @@ Every skill in this deployment, by what it is for. Start at a driver; it routes
|
|
|
42
42
|
|
|
43
43
|
| Skill | Use when... |
|
|
44
44
|
|-------|-------------|
|
|
45
|
-
| `skill:malloy-
|
|
45
|
+
| `skill:malloy-model-as-you-go` | After answering a question, writing down what it assumed: a `#(doc)`'d field in the model, an `extend` in the notebook, or a stated assumption plus snippet, depending on what the session can write |
|
|
46
|
+
| `skill:malloy-analyze` | Open-ended exploration with no intent to keep anything: profiling, hypotheses, views |
|
|
46
47
|
| `skill:malloy-charts` | Chart selection and renderer reference for Malloy visualizations |
|
|
47
48
|
| `skill:malloy-notebooks` | Building Malloy notebooks (.malloynb) |
|
|
48
49
|
| `skill:malloy-analysis-report` | Combining validated queries into a notebook report or dashboard |
|
|
@@ -1,7 +1,7 @@
|
|
|
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.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
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.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
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.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -24,7 +24,7 @@ Do NOT build the notebook in the same turn as `execute_query`. Explain first, th
|
|
|
24
24
|
|
|
25
25
|
## Filters are inherited from the model, don't declare them in the report
|
|
26
26
|
|
|
27
|
-
Reports do not (and cannot) define their own filters. If the source
|
|
27
|
+
Reports do not (and cannot) define their own filters. If the source declares `given:` parameters (or legacy `#(filter)` annotations), Publisher renders the controls, parses caller parameters, and applies them server-side automatically: the report inherits and displays them with no extra work. If the analysis needs a knob the source doesn't expose, the right move is to add a `given:` to the source itself, not to wedge a filter widget into the report. `#(filter)` is deprecated in favour of native Malloy `given:` parameters. Do not add new `#(filter)` annotations; the two exceptions are `required` and `implicit`, which `given:` cannot cover yet. See `skill:malloy-model` § Legacy: Parameterizable Filters. For curated notebooks with their own per-notebook filter UI on top of the model, see `skill:malloy-notebooks` instead.
|
|
28
28
|
|
|
29
29
|
## What goes in the report
|
|
30
30
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-analyze
|
|
3
|
-
description:
|
|
4
|
-
version: 0.1.
|
|
3
|
+
description: Open-ended exploration with no specific question to answer, and building views/dashboards/notebooks on an existing model. Use when the user asks "what's interesting?", "explore this data", "find insights", "look for patterns", "build a dashboard", or "create views". Not for a specific data question - answering one is `malloy-analysis`, and writing down what the answer assumed is `malloy-model-as-you-go`. For exploration start at Step 1; for views on an existing model jump to View Patterns.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -254,7 +254,9 @@ A notebook is also the home for a polished, narrated report: alternate `>>>markd
|
|
|
254
254
|
|
|
255
255
|
### Interactive Filters
|
|
256
256
|
|
|
257
|
-
**Notebooks do NOT define filters themselves.** When you import a model, the model's
|
|
257
|
+
**Notebooks do NOT define filters themselves.** When you import a model, the model's runtime parameters are **inherited and displayed automatically**: the publisher renders the controls, parses caller parameters, and applies them server-side. You don't redeclare them in the consumer. If the analysis needs a knob the model doesn't expose, the right move is to add it to the source itself, not to wedge filtering into the consumer.
|
|
258
|
+
|
|
259
|
+
**Declare that knob as a `given:`.** `#(filter)` is deprecated in favour of native Malloy `given:` parameters. Do not add new `#(filter)` annotations; the two exceptions are `required` and `implicit`, which `given:` cannot cover yet. See `skill:malloy-model` § Legacy: Parameterizable Filters.
|
|
258
260
|
|
|
259
261
|
The notebook-level `##(filters)` annotation and the dimension-level `#(filter) {"type": "..."}` JSON-blob form are **unsupported legacy syntax**, don't use them. The only supported form is `#(filter) name=... dimension=... type=...` declared above the source.
|
|
260
262
|
|
|
@@ -1,7 +1,7 @@
|
|
|
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.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-dashboards
|
|
3
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.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -31,8 +31,9 @@ Scanned at a glance is a dashboard; read top to bottom is a notebook.
|
|
|
31
31
|
`malloy_getContext` if you have it, otherwise the REST model endpoint or the `.malloy` files.
|
|
32
32
|
Never guess a name. A guessed field in a query fails the whole package load, not just that one
|
|
33
33
|
dashboard; a guessed tile or suggest source is quieter, and only shows up in the package warnings.
|
|
34
|
-
2. **
|
|
35
|
-
|
|
34
|
+
2. **PICK THE VIEWS TO SHOW.** A dashboard is `## artifact { tiles=[…] }` naming existing views, so
|
|
35
|
+
this is the design step: which views, how wide each sits, what each is called. There is one form,
|
|
36
|
+
so there is no form to choose.
|
|
36
37
|
3. **DECLARE THE GIVENS** the dashboard will filter by, in the model (usually `givens.malloy`), with
|
|
37
38
|
their control tags: see "Filter controls" below for the syntax and what each tag renders as. Skip
|
|
38
39
|
if they already exist, since a given is a model concern and dashboards share them.
|
|
@@ -40,7 +41,11 @@ Scanned at a glance is a dashboard; read top to bottom is a notebook.
|
|
|
40
41
|
Import every given it filters by, and every source or query any of those givens names in a
|
|
41
42
|
`suggest`. Both are per-file, and getting the suggest wrong does not error: the control still
|
|
42
43
|
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
|
+
this control". The package warnings name it too. **A `suggest` naming a `query=` needs that
|
|
45
|
+
query's own source imported as well**, because an import is not transitive: the query resolves by
|
|
46
|
+
name, so the file compiles and the package loads, but running the picker fails with
|
|
47
|
+
`Undefined source '<name>'`. The package warnings name this one too, saying which source to
|
|
48
|
+
import. Import the source that suggest query reads, not just the query.
|
|
44
49
|
5. **COMPILE IT** with `malloy_compile` (or `POST …/models/<path>/compile`), against the source text,
|
|
45
50
|
before you save, at the path the file will have. **Editing one that already exists needs
|
|
46
51
|
`"scope": "file"`**, which compiles your source AS that file; the default appends it instead, so
|
|
@@ -48,7 +53,7 @@ Scanned at a glance is a dashboard; read top to bottom is a notebook.
|
|
|
48
53
|
already-defined errors that reads as broken Malloy rather than a wrong scope. **Editing a shared
|
|
49
54
|
include wants `"scope": "package"`**, which recompiles every file as saved: `file` only checks the
|
|
50
55
|
one you are editing, so renaming a source in `_shared.malloy` passes it while breaking every
|
|
51
|
-
|
|
56
|
+
dashboard that imports it. A clean compile is not a working dashboard: some tag mistakes surface
|
|
52
57
|
at step 6, and some only when you look at the page in step 7. (The third scope, `append`, is the
|
|
53
58
|
default and is what a not-yet-saved file gets.)
|
|
54
59
|
6. **SAVE IT, RELOAD, AND READ THE MANIFEST AND THE WARNINGS.** `malloy_reloadPackage`, or
|
|
@@ -60,9 +65,63 @@ Scanned at a glance is a dashboard; read top to bottom is a notebook.
|
|
|
60
65
|
and its `query` is the name to run in step 7. See "Read the lint" below.
|
|
61
66
|
7. **OPEN IT AND LOOK.** Not optional; see "What 'done' means".
|
|
62
67
|
|
|
63
|
-
## The
|
|
68
|
+
## The form
|
|
64
69
|
|
|
65
|
-
|
|
70
|
+
A dashboard is `## artifact { tiles=[…] }` at model level: named views, each run as its own query, laid
|
|
71
|
+
out by Publisher into the grid `# dashboard { columns=N }` names.
|
|
72
|
+
|
|
73
|
+
```malloy
|
|
74
|
+
##! experimental.givens
|
|
75
|
+
## artifact { title="Storefront overview" tiles=["overview -> kpis", "overview -> revenue_trend", "overview -> revenue_by_state"] } dashboard { columns=12 }
|
|
76
|
+
import { scoped_sales } from './_shared.malloy'
|
|
77
|
+
import { products } from '../storefront.malloy'
|
|
78
|
+
import { CATEGORY, SINCE } from '../givens.malloy'
|
|
79
|
+
|
|
80
|
+
// Layout goes on the VIEW, and a thin re-declaration is the place to put it: the
|
|
81
|
+
// modelled view keeps its chart tag, and this decides how wide it sits here.
|
|
82
|
+
source: overview is scoped_sales extend {
|
|
83
|
+
# colspan=12
|
|
84
|
+
# label="Key figures"
|
|
85
|
+
# big_value
|
|
86
|
+
view: kpis is {
|
|
87
|
+
aggregate:
|
|
88
|
+
# label="Revenue"
|
|
89
|
+
# currency
|
|
90
|
+
total_sales
|
|
91
|
+
# label="Orders"
|
|
92
|
+
order_count
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# colspan=8
|
|
96
|
+
# break
|
|
97
|
+
# label="Revenue by month"
|
|
98
|
+
view: revenue_trend is sales_by_month
|
|
99
|
+
|
|
100
|
+
# colspan=4
|
|
101
|
+
# label="Revenue by state"
|
|
102
|
+
view: revenue_by_state is sales_by_state
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Model-level because there is no query of its own to hang a `#` tag on, and model-level for a second
|
|
107
|
+
reason: tiles run as separate queries, which is the only way a page can span unrelated sources. A
|
|
108
|
+
nest's pipeline starts from its own query's source and there is no way to combine two.
|
|
109
|
+
|
|
110
|
+
Three things the form costs, so you are not surprised by them:
|
|
111
|
+
|
|
112
|
+
- **A tile expression is a string in an annotation, so the compiler never checks it.** Rename a view
|
|
113
|
+
and the file still compiles; the tile fails at package load. Read the lint (step 6).
|
|
114
|
+
- **No per-parent-row grouping.** There is no parent query to repeat a grid over.
|
|
115
|
+
- **Filtering lives in what the tiles name**, not on the page. That is the shared include's job,
|
|
116
|
+
below.
|
|
117
|
+
|
|
118
|
+
### Also served: `# artifact` on a `query:`
|
|
119
|
+
|
|
120
|
+
One query whose result is the whole page, laid out by `@malloydata/render` from the query's own
|
|
121
|
+
`# dashboard` tag. This is Malloy's rendering feature, the same thing a notebook cell shows, not a
|
|
122
|
+
second way to build a dashboard, and it cannot span sources. Publisher serves it, so you will meet it
|
|
123
|
+
in existing packages, and the layout tags below are shared with it. Do not author a new dashboard
|
|
124
|
+
this way.
|
|
66
125
|
|
|
67
126
|
```malloy
|
|
68
127
|
##! experimental.givens
|
|
@@ -108,25 +167,16 @@ query: overview is order_items -> {
|
|
|
108
167
|
|
|
109
168
|
The `#"` line above the tag is a doc comment, and it is the page's description. If you leave `title=`
|
|
110
169
|
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
|
|
170
|
+
page. **It belongs to a query, so it only works on the one-query form.** Putting a `#"` above a
|
|
112
171
|
model-level `##` tag fails the whole package load with "Object annotation not connected to any
|
|
113
|
-
object",
|
|
172
|
+
object", so a dashboard has no description as a result.
|
|
114
173
|
|
|
115
|
-
|
|
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
|
-
```
|
|
174
|
+
### Where the filtering goes
|
|
125
175
|
|
|
126
|
-
A
|
|
176
|
+
A dashboard has no query, so the filtering it applies must live in what it composes: a source that
|
|
127
177
|
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
|
|
129
|
-
imports**: a given the
|
|
178
|
+
treats as a shared include rather than a dashboard. **It has to apply every given the dashboard
|
|
179
|
+
imports**: a given the dashboard imports but nothing references gets no control, silently, at reload
|
|
130
180
|
200 with no warning. Note `SINCE` is a `date` rather than a `filter<>`, so it compares with `>=`
|
|
131
181
|
rather than `~`. Save the include before you compile the dashboard that imports it, since an importer
|
|
132
182
|
compiled against a sibling that is not on disk fails with an `import-error`.
|
|
@@ -141,42 +191,56 @@ source: scoped_sales is order_items extend {
|
|
|
141
191
|
}
|
|
142
192
|
```
|
|
143
193
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
194
|
+
**`# dashboard { columns=N }` is the one spelling of the grid width**, on both forms, beside the
|
|
195
|
+
artifact tag. Anything else inside the artifact tag is a package warning naming it, which is what you
|
|
196
|
+
will see if you write `dashboard_columns=N`: nothing reads it, and the grid would otherwise fall back
|
|
197
|
+
to the default width in silence.
|
|
148
198
|
|
|
149
|
-
|
|
150
|
-
|
|
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.
|
|
199
|
+
A tile keeps its view's own field names on axes and column headers. `# label` titles the tile; to label
|
|
200
|
+
what is inside it, label the fields in the view.
|
|
156
201
|
|
|
157
202
|
## Layout: the four tags that make a page line up
|
|
158
203
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
204
|
+
Cards and tiles share one grid, and the same four tags work on both forms: on a `# dashboard` query
|
|
205
|
+
the renderer reads them off each nest, and on a dashboard's `tiles=[…]` Publisher reads them off the
|
|
206
|
+
view each tile names. Copy this recipe and use the same count on every dashboard in the package so
|
|
207
|
+
they read as one product:
|
|
162
208
|
|
|
163
209
|
1. **`columns=12`** on the `# dashboard` tag. Twelve divides by 2, 3, 4 and 6, so a row is even with
|
|
164
210
|
three cards or four.
|
|
165
211
|
2. **A `# colspan` on every card and tile, summing to 12 per row.** Four cards at 3, three at 4, two
|
|
166
212
|
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.
|
|
213
|
+
of the width, which is too narrow for a line chart to draw in at all. A colspan wider than
|
|
214
|
+
`columns` is clamped, and the package warnings say so.
|
|
168
215
|
3. **`# break` on the first tile after the cards.** Otherwise it flows into the columns left beside
|
|
169
216
|
the cards and the next tile wraps. Not needed per row: once a row sums to 12 the next item wraps
|
|
170
217
|
on its own.
|
|
171
|
-
4. **`# label="
|
|
172
|
-
nest, whose column headers are field names too. The heading is otherwise the view's or
|
|
173
|
-
name, and a wide table full of `total_sales` and `order_item_count` is the most visible
|
|
174
|
-
between a rough page and a finished one. A view
|
|
175
|
-
the tile, but its own field names still reach the chart axes and the column headers, so
|
|
176
|
-
if you want those
|
|
218
|
+
4. **`# label="..."` on every nest, tile view, and aggregate**, including the aggregates inside a table
|
|
219
|
+
nest, whose column headers are field names too. The heading is otherwise derived from the view's or
|
|
220
|
+
field's name, and a wide table full of `total_sales` and `order_item_count` is the most visible
|
|
221
|
+
thing between a rough page and a finished one. A view referenced **by name** is the exception: you
|
|
222
|
+
can label the tile, but its own field names still reach the chart axes and the column headers, so
|
|
223
|
+
label the fields in the view if you want those too.
|
|
224
|
+
|
|
225
|
+
On a dashboard, all four go on the view a tile names, which is why a thin re-declaration
|
|
226
|
+
(`view: revenue_trend is sales_by_month`) is the place to put them: the modelled view stays reusable
|
|
227
|
+
and each page decides its own widths. `# subtitle="..."` and `# borderless` go there too and are the
|
|
228
|
+
rest of the set; a tile reads all five exactly as a `# dashboard` nest child does.
|
|
229
|
+
|
|
230
|
+
**Do not forget `columns=` itself.** What you get without it differs by form, and neither is the page
|
|
231
|
+
you drew. On a dashboard it falls back to a narrow default and every colspan is clamped to it, which
|
|
232
|
+
is the usual reason a page you laid out comes out one item per row, and the package warnings name it.
|
|
233
|
+
On a `# dashboard` query there is no grid at all: the cards flow side by side at their natural widths
|
|
234
|
+
and every colspan is ignored. No package warning says so, because the renderer reports it at query
|
|
235
|
+
time. See "Losing the grid".
|
|
177
236
|
|
|
178
237
|
Then the traps:
|
|
179
238
|
|
|
239
|
+
- **A KPI row is authored differently on the two forms.** On a `# dashboard` query a top-level
|
|
240
|
+
`aggregate:` measure IS the card, and nesting a `# big_value` view to get one renders it embedded,
|
|
241
|
+
as full-width bars in a single tile. A dashboard has no top-level aggregates, since a tile is one
|
|
242
|
+
whole result, so there a `# big_value` view IS the KPI row and renders as one. This is the only
|
|
243
|
+
place the two forms need different Malloy for the same picture.
|
|
180
244
|
- **No `# size=fill` on a dashboard tile.** Inside a dashboard it measures against the container the
|
|
181
245
|
whole grid was handed, not the tile, so it yields a chart thousands of pixels tall. Tiles already
|
|
182
246
|
size to their colspan.
|
|
@@ -196,9 +260,9 @@ The last two are upstream renderer behavior, cheap to work around in the model.
|
|
|
196
260
|
|
|
197
261
|
The same tags govern a `# dashboard` **view** run in a notebook cell, since both surfaces render
|
|
198
262
|
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
|
|
200
|
-
|
|
201
|
-
|
|
263
|
+
Height is the one thing the surface decides: a one-query page renders at its natural height, a
|
|
264
|
+
dashboard's tiles are each capped, and in a notebook a chart cell is capped and a table cell hugs its
|
|
265
|
+
rows.
|
|
202
266
|
|
|
203
267
|
## The rules that actually bite
|
|
204
268
|
|
|
@@ -207,7 +271,7 @@ its rows.
|
|
|
207
271
|
collides with an imported `regions` source).
|
|
208
272
|
- **Importing a given is what makes it bindable.** Malloy's given namespace is per-file. A given the
|
|
209
273
|
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
|
|
274
|
+
that references it lives up an import chain. A dashboard must import the givens its tiles use.
|
|
211
275
|
- **A suggest's source or query has to resolve in the dashboard file too.** `suggest { source=products … }`
|
|
212
276
|
means the dashboard imports `products`.
|
|
213
277
|
- **A model-level `##` tag must be on one line.** Wrapping one always breaks it, but how you find
|
|
@@ -318,15 +382,14 @@ either, for a different reason.
|
|
|
318
382
|
|
|
319
383
|
## Losing the grid
|
|
320
384
|
|
|
321
|
-
A
|
|
322
|
-
|
|
385
|
+
A one-query page can come out with its layout wrong in two visibly different ways, and the reload is
|
|
386
|
+
200 and the manifest reports the column count you asked for in both.
|
|
323
387
|
|
|
324
|
-
**Not a dashboard at all: one plain nested table**, every `# colspan` and `# break` dropped
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
is immune because its layout comes from the manifest rather than a re-parse.
|
|
388
|
+
**Not a dashboard at all: one plain nested table**, every `# colspan` and `# break` dropped, because a
|
|
389
|
+
`f'…'` filter literal in a `givens { … }` block shares a line with `# dashboard`. Put `# dashboard` on
|
|
390
|
+
its own line: writing it first on the line does not help, a plain `'Outerwear'` or a bare date is
|
|
391
|
+
fine, and a `tiles=[…]` dashboard is immune because its layout comes from the manifest rather than a
|
|
392
|
+
re-parse.
|
|
330
393
|
|
|
331
394
|
**A dashboard, but nothing lines up**: you wrote `# colspan` without `columns=N`, so the items flow
|
|
332
395
|
side by side at their natural widths instead of aligning to a grid.
|
|
@@ -355,7 +418,9 @@ Package warnings after a reload are the dashboard's test suite. Fix all of them:
|
|
|
355
418
|
- `filters by given "X", which this file does not import, so no control is shown for it`: the trap
|
|
356
419
|
under "Importing a given is what makes it bindable", which the lint now names for you, with the file
|
|
357
420
|
to fix.
|
|
358
|
-
- A tile that does not resolve to a real view
|
|
421
|
+
- A tile that does not resolve to a real view; a non-positive `# dashboard { columns }`; a tile view's
|
|
422
|
+
`# colspan` that is not a positive integer or is wider than the grid; and any property inside the
|
|
423
|
+
artifact tag Publisher does not read, `dashboard_columns=` included.
|
|
359
424
|
|
|
360
425
|
Findings carry a `severity`, but `warn` is the ordinary default and tells you nothing about how bad
|
|
361
426
|
one is. Read the text, not the severity and not the count. One message is worth recognising because
|
|
@@ -401,9 +466,8 @@ the warning list.
|
|
|
401
466
|
- Each control renders as the widget you intended (a select shows options; a slider is a slider),
|
|
402
467
|
and changing one changes the numbers.
|
|
403
468
|
- If you added a `# drill`, you clicked it and landed where you meant to, with the given seeded.
|
|
404
|
-
-
|
|
405
|
-
|
|
406
|
-
than leaving a gap. Nothing is clipped, no tile is thousands of pixels tall, and no legend or card
|
|
469
|
+
- Every card and tile carries a colspan, each row's colspans sum to `columns`, and the rows end flush
|
|
470
|
+
with each other. Nothing is clipped, no tile is thousands of pixels tall, and no legend or card
|
|
407
471
|
label ends in `…`.
|
|
408
472
|
|
|
409
473
|
## Reference
|
|
@@ -1,7 +1,7 @@
|
|
|
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.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
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.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -160,7 +160,7 @@ The user will:
|
|
|
160
160
|
- **Remove** fields they don't need.
|
|
161
161
|
- **Change** priorities.
|
|
162
162
|
|
|
163
|
-
Once the definitions are confirmed, write them into the `.malloy` model (see your modeling workflow). Use `#(doc)` annotations to document sources and fields, and
|
|
163
|
+
Once the definitions are confirmed, write them into the `.malloy` model (see your modeling workflow). Use `#(doc)` annotations to document sources and fields, and `given:` parameters to declare runtime-filterable dimensions where appropriate. `#(filter)` is deprecated in favour of native Malloy `given:` parameters. Do not add new `#(filter)` annotations; the two exceptions are `required` and `implicit`, which `given:` cannot cover yet. See `skill:malloy-model` § Legacy: Parameterizable Filters. Keep the confirmed definitions in the conversation; there is no separate plan-file store.
|
|
164
164
|
|
|
165
165
|
## Data-driven proposals
|
|
166
166
|
|
|
@@ -1,7 +1,7 @@
|
|
|
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.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-document
|
|
3
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.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -15,7 +15,7 @@ Add `#(doc)` tags to describe sources and fields in plain language so they are e
|
|
|
15
15
|
| Tag | Purpose | Goes on |
|
|
16
16
|
|-----|---------|---------|
|
|
17
17
|
| `#(doc)` | Plain-language description for natural-language search | source, dimension, measure, view, join |
|
|
18
|
-
| `#(filter)` |
|
|
18
|
+
| `#(filter)` | Deprecated, prefer `given:`. Parameterizable filter (runtime/modeling concern, see `malloy-model`) | source |
|
|
19
19
|
|
|
20
20
|
`#(doc)` is a standard Malloy annotation. It documents a field or source with a human-readable description that downstream tools can surface and search against.
|
|
21
21
|
|
|
@@ -76,9 +76,11 @@ These are governed models: a threshold nobody confirmed is an assumption, and an
|
|
|
76
76
|
|
|
77
77
|
Do not hedge measured facts: `avg_energy is avg(energy)` needs no caveat. Hedge only where a domain expert could reasonably choose differently.
|
|
78
78
|
|
|
79
|
-
## #(filter): see `malloy-model`
|
|
79
|
+
## #(filter): deprecated, see `malloy-model`
|
|
80
80
|
|
|
81
|
-
`#(filter)` is
|
|
81
|
+
`#(filter)` is deprecated in favour of native Malloy `given:` parameters. Do not add new `#(filter)` annotations; the two exceptions are `required` and `implicit`, which `given:` cannot cover yet. See `skill:malloy-model` § Legacy: Parameterizable Filters.
|
|
82
|
+
|
|
83
|
+
`#(filter)` is also a `#(...)`-shaped annotation, but unlike `#(doc)` it's a **runtime/modeling construct**: it shapes governance, query latency, and correctness, not discoverability. The full reference (syntax, filter types, `required` / `implicit` flags, and when each applies) lives in `malloy-model` § Legacy: Parameterizable Filters alongside the other source-authoring constructs, next to the `given:` guidance that replaces it.
|
|
82
84
|
|
|
83
85
|
One rule worth knowing here: filters live on the source, never on the consumer. Ad-hoc reports and notebooks that import a source inherit its filters automatically; they do not (and cannot) declare new ones.
|
|
84
86
|
|
|
@@ -1,7 +1,7 @@
|
|
|
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.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
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.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
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.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-gotchas-rendering
|
|
3
3
|
description: Common Malloy renderer annotation mistakes. Read BEFORE adding chart annotations, formatting tags, or building dashboards. Covers tag syntax, scale rules, sparkline setup, and big_value patterns.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-html-data-app-embedding
|
|
3
3
|
description: Embed an in-package HTML data app into a host page or another application, including auto-sizing and auth. Read when embedding a Publisher page via Publisher.embed.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-html-data-app-runtime
|
|
3
3
|
description: Write the JavaScript that drives an in-package HTML data app, calling Publisher.query, building queries from filter state, and handling results and errors. Read before writing the page's data code.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-html-data-apps
|
|
3
3
|
description: Build or modify an in-package HTML data app for a Malloy Publisher package (a public/ directory the package serves). Use when the user wants a hand-authored HTML dashboard or web page backed by a package's Malloy models, with no build step.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-lookml-review
|
|
3
3
|
description: Analyze LookML files as prior art for Malloy modeling. Used during Step 1 (DISCOVER) when .lkml files are present. Coordinates reference files that extract business logic, relationships, and curation decisions. Works with or without a database connection.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-materialization
|
|
3
3
|
description: Add and debug Malloy Persistence materializations in a package - persist an expensive source so queries read a pre-built table. Read this whenever the user wants to materialize a source, add a persist annotation, speed up a slow source, or asks why a persist source isn't building.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-materialization-tuning
|
|
3
3
|
description: Optimize a package's Malloy Persistence materializations for cost and performance using the malloy-pub CLI and the materialization history. Recommend what to persist, what to stop persisting, and how to schedule/scope it. Use when the user asks to make a package cheaper or faster, tune persistence, decide what to materialize, or review persist/schedule choices.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-model
|
|
3
3
|
description: Build Malloy semantic models with base source and joined source files. Use when creating or modifying .malloy files, user asks to "create a malloy model", "add dimensions", "add measures", "create a source", or any Malloy model authoring task.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -169,9 +169,21 @@ source: customer_health is customers extend {
|
|
|
169
169
|
| **Dimensions** | Intrinsic to this table only | Cross-source (require joins) |
|
|
170
170
|
| **Measures** | Single-table aggregations | Cross-source aggregations |
|
|
171
171
|
| **Joins** | None (or only lookup joins intrinsic to the source) | Defines relationships between base sources |
|
|
172
|
-
| **Views** | None
|
|
172
|
+
| **Views** | None, in schema-first (see below) | None, in schema-first (see below) |
|
|
173
173
|
| **One per** | Physical table or computed source | Analytical domain |
|
|
174
174
|
|
|
175
|
+
**The "no views" rule is schema-first only.** A schema-first model is built before anyone
|
|
176
|
+
has asked a question, so any view in it is a guess. It does **not** apply to the
|
|
177
|
+
analysis-first workflow (`skill:malloy-model-as-you-go`), where every view is a
|
|
178
|
+
question that was asked and verified - there, **saving a view or a dashboard is the right
|
|
179
|
+
call**, in the model file next to the measures it uses. Analysis-first still models
|
|
180
|
+
everything else properly: documented dimensions, measures, and joins.
|
|
181
|
+
|
|
182
|
+
**Two more rules here are schema-first only.** Analysis-first should skip access modifiers
|
|
183
|
+
and curation - there is no discovery surface to curate when every field was paid for by a
|
|
184
|
+
question - and skip one-file-per-table, keeping a single domain file until it genuinely
|
|
185
|
+
gets unwieldy.
|
|
186
|
+
|
|
175
187
|
## Key Rules
|
|
176
188
|
|
|
177
189
|
- **Every `dimension:` needs `name is expr`**: a bare `dimension: species` is a parse error. Raw columns are queryable directly in `group_by` / `select`; only declare a `dimension:` to derive or rename a field.
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: malloy-model-as-you-go
|
|
3
|
+
description: After answering a data question, write down what the answer assumed so the next reader can trust the number. A field with a #(doc) in the model when you can edit it, an extend in the notebook when you can only author reports, or a stated assumption plus a Malloy snippet when you can only chat. Use after every answered question that rested on a judgment call, and whenever a question is asked against tables that have no model yet.
|
|
4
|
+
version: 0.1.10
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!--
|
|
8
|
+
Copyright (c) Credible Data Inc.
|
|
9
|
+
SPDX-License-Identifier: MIT
|
|
10
|
+
-->
|
|
11
|
+
|
|
12
|
+
# Model as you go
|
|
13
|
+
|
|
14
|
+
An analysis that lives in a chat transcript is not reproducible. The numbers were right, and
|
|
15
|
+
six weeks later nobody can say what "revenue" excluded, which of four timestamps was the order
|
|
16
|
+
date, or whether the last period was complete. The work is unauditable, so it gets redone.
|
|
17
|
+
|
|
18
|
+
The fix is to write the assumptions down while the query is still in front of you, in the
|
|
19
|
+
most durable place your session can write. Answer the question, codify what the answer
|
|
20
|
+
assumed, answer the next one. After a handful of questions there is a model, or a notebook,
|
|
21
|
+
where every definition exists because a real question needed it, and every judgment call is
|
|
22
|
+
on the record.
|
|
23
|
+
|
|
24
|
+
This skill is the codify step. `skill:malloy-analysis` answers the question; this skill
|
|
25
|
+
decides what to write down afterwards, and where.
|
|
26
|
+
|
|
27
|
+
> **Tool names** are bare here - `get_context`, `execute_query`, `search_database_schema`. The
|
|
28
|
+
> exact prefixed name depends on the host; match against the tools you actually have.
|
|
29
|
+
|
|
30
|
+
## The loop
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
QUESTION → ANSWER → CODIFY ⟲
|
|
34
|
+
↑___________|
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**The user gets a real answer on question one.** If three tool calls have gone by without
|
|
38
|
+
producing an insight they can read, you have drifted into modelling for its own sake. Stop and
|
|
39
|
+
answer something.
|
|
40
|
+
|
|
41
|
+
## 1. Answer the question, with `skill:malloy-analysis`
|
|
42
|
+
|
|
43
|
+
This skill starts when someone asks a data question. **That question is the unit of work, and
|
|
44
|
+
it is theirs.** Do not widen it into a modelling project, and do not swap it for a more
|
|
45
|
+
interesting one you found on the way.
|
|
46
|
+
|
|
47
|
+
**A broad ask is still an ask.** "Analyse the sales data" is a question whose subject is given
|
|
48
|
+
and whose metric is not. Pick the most obvious question about that subject, say in one line
|
|
49
|
+
which one you picked, and answer it. Never open with a row-count table and a menu of
|
|
50
|
+
"analytical domains"; the menu is worth less than the first real answer would have been.
|
|
51
|
+
|
|
52
|
+
Load `skill:malloy-analysis` and follow it: discover the model, construct the query, run it,
|
|
53
|
+
verify it, present it. Two of its rules matter most here:
|
|
54
|
+
|
|
55
|
+
- **Every number you present comes out of a query.** Adding up the rows of a `limit: 15` table
|
|
56
|
+
by hand drops everything below the cut, and nobody can re-run it.
|
|
57
|
+
- **Your first result is a draft.** Work `skill:malloy-analysis-pitfalls` before presenting.
|
|
58
|
+
|
|
59
|
+
### Name the decisions the answer rests on
|
|
60
|
+
|
|
61
|
+
**This is what makes the analysis auditable, and it is the step most easily skipped.**
|
|
62
|
+
|
|
63
|
+
Almost every query needs at least one judgment call the data cannot settle. Which of four
|
|
64
|
+
timestamps is "the order date". Whether returns count as revenue. Whether to count lines or
|
|
65
|
+
orders. Whether a partial final period belongs in a trend.
|
|
66
|
+
|
|
67
|
+
A judgment call made silently becomes a hidden assumption. It will be wrong for someone
|
|
68
|
+
eventually, and by then nobody remembers it was a choice rather than a fact. So make the call,
|
|
69
|
+
run with it, and **say what you chose and what you rejected**, with the other number attached
|
|
70
|
+
whenever it is cheap to get:
|
|
71
|
+
|
|
72
|
+
> Revenue here excludes cancelled **and** returned orders - that's $8.10M. Counting returns as
|
|
73
|
+
> revenue and netting them separately gives $9.18M. I went with the stricter one; say if your
|
|
74
|
+
> reporting does it the other way.
|
|
75
|
+
|
|
76
|
+
Not every choice rises to this. Raise it when **any** of these holds:
|
|
77
|
+
|
|
78
|
+
| Raise it when | Because |
|
|
79
|
+
|---|---|
|
|
80
|
+
| The alternative changes the number materially | The user would answer differently depending on which they meant |
|
|
81
|
+
| You are about to codify it | It stops being your choice and becomes everyone's definition |
|
|
82
|
+
| A reasonable analyst would pick the other one | It is a convention, not a fact |
|
|
83
|
+
|
|
84
|
+
Otherwise state it in a clause and move on. **Asking about everything is as bad as asking about
|
|
85
|
+
nothing**; it turns the loop into a form and trains the reader to skim past it. And **give the
|
|
86
|
+
number under each option, not an abstract question**: "$8.10M excluding them, $9.18M
|
|
87
|
+
including" lets the user answer in one word.
|
|
88
|
+
|
|
89
|
+
## 2. When there is no model yet
|
|
90
|
+
|
|
91
|
+
If the tables have no model, define only enough to run the first query. One line is normal:
|
|
92
|
+
|
|
93
|
+
```malloy
|
|
94
|
+
source: order_items is my_conn.table('ecommerce.order_items')
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Read the columns with `run: source -> { select: * limit: 3 }`.** Prefer it to a schema
|
|
98
|
+
listing: it returns the columns *and* real values, and the values are what catch the surprises
|
|
99
|
+
a column list hides - a date stored as a string, a `total` that excludes tax, a metric column
|
|
100
|
+
that is null on every row.
|
|
101
|
+
|
|
102
|
+
If a schema tool returns something you cannot explain (no columns, or no tables for a filter
|
|
103
|
+
you can see matches), that is a bug in the tool. Report it. Do not write the workaround into
|
|
104
|
+
your model or your notes as though it were a property of the data.
|
|
105
|
+
|
|
106
|
+
No `primary_key:`, no dimensions, no measures, no joins, **not yet**. Those arrive in CODIFY,
|
|
107
|
+
each one paid for by a question that needed it. Adding fields because the table has them is the
|
|
108
|
+
habit this skill exists to break.
|
|
109
|
+
|
|
110
|
+
Add a join only when *this* question cannot be answered without it, then verify its cardinality
|
|
111
|
+
before trusting any aggregate (`group_by: fk, aggregate: n is count(), having: n > 1`).
|
|
112
|
+
|
|
113
|
+
If a model already exists, ground yourself in it with `get_context` and reuse what is there.
|
|
114
|
+
|
|
115
|
+
## 3. CODIFY: write the answer's assumptions down
|
|
116
|
+
|
|
117
|
+
Run this after *every* answered question, while the query is still in front of you. Skipping it
|
|
118
|
+
is how a session ends with a transcript and nothing else.
|
|
119
|
+
|
|
120
|
+
### Where it goes depends on what you can write
|
|
121
|
+
|
|
122
|
+
The same assumption lands in a different place depending on the session. Pick the **highest
|
|
123
|
+
rung your tools allow**, and never skip codifying because the top rung is out of reach.
|
|
124
|
+
|
|
125
|
+
| You can | Codify as | Who inherits it |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| **Edit the model files** (a local package, a draft package, a compile or reload tool) | A `dimension:`, `measure:`, `join_*:`, or `view:` with a `#(doc)`, in the `.malloy` file | Everyone who queries the model. **Confirm binding decisions first** (below) |
|
|
128
|
+
| **Author notebooks or reports, but not the model** (a viewer of a published package) | In the notebook: `source: orders_q is orders extend { measure: ... }` with the `#(doc)` above it, plus a markdown cell stating the assumption. A question worth re-asking becomes a cell | Readers of the report. State the decision in the cell, and say which definitions the model should adopt |
|
|
129
|
+
| **Only answer in chat** (no file or report tools) | The assumption stated in the answer, plus the Malloy snippet a modeler could paste: the `measure:` with its `#(doc)` | Nobody, until someone acts on it. That is why the snippet matters |
|
|
130
|
+
|
|
131
|
+
Tell the rungs apart by the tools you have, not by guessing at the user's role: a file-write or
|
|
132
|
+
compile tool means the top rung; a report or notebook tool without model edits means the middle
|
|
133
|
+
one; neither means the bottom.
|
|
134
|
+
|
|
135
|
+
### What to codify
|
|
136
|
+
|
|
137
|
+
Look at the query you just ran. For each dimension, measure, join, filter, **and for the shape
|
|
138
|
+
of the query itself**, ask **in this order**:
|
|
139
|
+
|
|
140
|
+
| Codify it when | Example |
|
|
141
|
+
|---|---|
|
|
142
|
+
| **A reader needs it to trust the number** | the cancelled/returned exclusion - without it every revenue figure reads high |
|
|
143
|
+
| **It encodes a business rule** | a regex parsing a messy column, a status mapping, tier cutoffs |
|
|
144
|
+
| **Another question would reuse it** | `revenue is sum(sale_price)` - everything about orders needs it |
|
|
145
|
+
| **It was hard to get right** | a window function, a multi-step derivation, a verified join |
|
|
146
|
+
|
|
147
|
+
The first row is the one that matters. A definition is worth keeping less because it saves
|
|
148
|
+
typing than because it stops the next reader misreading the number.
|
|
149
|
+
|
|
150
|
+
**Codify thin, and leave the ad-hoc behind**: filters tied to one finding, calculations that
|
|
151
|
+
answered exactly one question. A question that needed one measure codifies one measure, not the
|
|
152
|
+
neighbouring columns as well. An empty CODIFY is a fine outcome; say so and move on.
|
|
153
|
+
|
|
154
|
+
**Then say what you did**, in one line, every time:
|
|
155
|
+
|
|
156
|
+
> Codified: `revenue is sum(sale_price)` excluding cancelled and returned, `category` via the
|
|
157
|
+
> products join. Left ad-hoc: the `where: created_at > @2023` - just this question's window.
|
|
158
|
+
|
|
159
|
+
That line shows the model growing and gives the reader a place to object. Never codify
|
|
160
|
+
silently.
|
|
161
|
+
|
|
162
|
+
### A decision becomes binding the moment it enters the model
|
|
163
|
+
|
|
164
|
+
While a judgment call lives in one ad-hoc query it is yours, and stating it is enough. Once it
|
|
165
|
+
is in the model file it is the definition everyone inherits, and nobody downstream sees the
|
|
166
|
+
reasoning. So on the top rung, **stop, ask, and wait for an answer** before writing it down.
|
|
167
|
+
Reporting it afterwards ("I used the midpoint, say if you'd rather have the floor") is not
|
|
168
|
+
confirming it; by then it is already the default. On the middle rung, confirm when the report
|
|
169
|
+
will be shared; a private notebook is still yours.
|
|
170
|
+
|
|
171
|
+
This is not only about measures. Confirm anything that changes what later questions return:
|
|
172
|
+
|
|
173
|
+
| Confirm before codifying | Because it silently sets |
|
|
174
|
+
|---|---|
|
|
175
|
+
| A source-level `where:` | the scope of *every* later question against that source |
|
|
176
|
+
| A `measure:` definition | the default value of that metric for everyone |
|
|
177
|
+
| A `dimension:` that buckets, maps, or parses | which rows land in which group |
|
|
178
|
+
| A `join_one:`/`join_many:` and its grain | whether aggregates fan out |
|
|
179
|
+
| A saved `view:` | the shape people will re-run and cite |
|
|
180
|
+
|
|
181
|
+
Ask with the numbers attached:
|
|
182
|
+
|
|
183
|
+
> I want to make `net_revenue` exclude cancelled and returned. That makes it the default revenue
|
|
184
|
+
> number for every later question - $8.10M rather than $10.81M gross. Good, or does your
|
|
185
|
+
> reporting treat returns differently?
|
|
186
|
+
|
|
187
|
+
**One confirmation per decision, not per question.** Once the user has settled how revenue
|
|
188
|
+
treats returns, it is settled: reuse it and stop asking. And if the user has told you to stop
|
|
189
|
+
checking in, believe them; state each decision in a clause and keep going.
|
|
190
|
+
|
|
191
|
+
### Write it down twice
|
|
192
|
+
|
|
193
|
+
**In the model (or the notebook), as a `#(doc)` on the definition.** This is the part that
|
|
194
|
+
survives. Use `#(doc)`, never a `//` comment, for anything a consumer needs: `#(doc)` is
|
|
195
|
+
machine-readable, so it renders in the UI and retrieval tools read it, while a `//` comment
|
|
196
|
+
reaches nobody but whoever opens the file. Keep `//` for maintainer-only notes. Tag formats on
|
|
197
|
+
the definition too: bare `# currency` on the measure, any scale (`# currency=usd0m`) only in
|
|
198
|
+
views.
|
|
199
|
+
|
|
200
|
+
```malloy
|
|
201
|
+
#(doc) Order line items joined to products. Grain is one row per line, not per order.
|
|
202
|
+
source: order_items is my_conn.table('ecommerce.order_items') extend {
|
|
203
|
+
join_one: products is my_conn.table('ecommerce.products') on product_id = products.id
|
|
204
|
+
|
|
205
|
+
dimension:
|
|
206
|
+
#(doc) Canonical order date. Data runs 2019-01-05 to 2026-03-14, so the final year is PARTIAL - never present it as a full period.
|
|
207
|
+
order_date is created_at::date
|
|
208
|
+
|
|
209
|
+
measure:
|
|
210
|
+
#(doc) Revenue in USD, excluding cancelled and returned orders. Those are 25% of gross, so excluding them is not optional.
|
|
211
|
+
# currency
|
|
212
|
+
net_revenue is sum(sale_price) { where: status != 'Cancelled' and status != 'Returned' }
|
|
213
|
+
|
|
214
|
+
#(doc) Monthly revenue trend. The question asked on 2026-03-02.
|
|
215
|
+
# line_chart
|
|
216
|
+
view: revenue_trend is {
|
|
217
|
+
group_by: order_month is order_date.month
|
|
218
|
+
aggregate:
|
|
219
|
+
# currency=usd0m
|
|
220
|
+
net_revenue
|
|
221
|
+
order_by: order_month
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
One file per analytical domain, named for it: `order_revenue.malloy`, not `model.malloy`. It
|
|
227
|
+
grows monotonically across the session.
|
|
228
|
+
|
|
229
|
+
**In a notes file, as the reasoning.** Keep an `analysis-notes.md` beside the model (or a
|
|
230
|
+
markdown cell in the notebook) recording each question, what it found, what got codified and
|
|
231
|
+
what was left ad-hoc, and every verification finding. The model carries the *what*; the notes
|
|
232
|
+
carry the *why* and the evidence. It is also what lets you resume after losing context.
|
|
233
|
+
|
|
234
|
+
### Save the view when a question is worth re-asking
|
|
235
|
+
|
|
236
|
+
A saved `view:` turns "we answered that once" into "re-run it". A trend wanted again next
|
|
237
|
+
month belongs in the file as a `view:` with its chart tag (`skill:malloy-charts`); views wanted
|
|
238
|
+
side by side belong in a notebook (`skill:malloy-notebooks`), or in a dashboard surface if your
|
|
239
|
+
host has one. A genuine one-off does not.
|
|
240
|
+
|
|
241
|
+
> **This departs from `skill:malloy-model` on purpose.** Its "no views in source files" rule
|
|
242
|
+
> assumes a schema-first model, written before anyone asked a question, so its views would be
|
|
243
|
+
> guesses. Here every view is a question that was asked and verified. Two more of its rules do
|
|
244
|
+
> not apply either: skip access modifiers and curation (there is no discovery surface to curate
|
|
245
|
+
> when every field was paid for by a question), and keep one domain file rather than one file
|
|
246
|
+
> per table until it genuinely gets unwieldy. Everything else in `skill:malloy-model` applies:
|
|
247
|
+
> `#(doc)` on every field, verified join cardinality, `nullif` on division, a `given:` for a
|
|
248
|
+
> runtime parameter.
|
|
249
|
+
|
|
250
|
+
### Then loop
|
|
251
|
+
|
|
252
|
+
Go back to the question, and let what you just found sharpen the next one:
|
|
253
|
+
|
|
254
|
+
> Revenue is concentrated in three categories. Worth asking whether that's new - want the same
|
|
255
|
+
> cut by year?
|
|
256
|
+
|
|
257
|
+
## When the analysis is going to be shared
|
|
258
|
+
|
|
259
|
+
Docs are not on this list; they happen in CODIFY, as you go. When the user wants to hand it
|
|
260
|
+
over:
|
|
261
|
+
|
|
262
|
+
1. **Names.** Rename anything whose name only made sense inside one question.
|
|
263
|
+
2. **Doc pass.** Re-read the `#(doc)` lines for anything that drifted as the model grew. Check
|
|
264
|
+
grain, units, and null handling are each stated somewhere.
|
|
265
|
+
3. **Re-verify.** Re-run the session's key queries against the finished sources. If a number
|
|
266
|
+
moved, something was codified wrong. This is the check that proves reproducibility; do not
|
|
267
|
+
skip it.
|
|
268
|
+
4. **Structure**, only if one file has genuinely become unwieldy: base sources per table plus a
|
|
269
|
+
joined source per domain, per `skill:malloy-model`. Being shared is not itself a reason.
|
|
270
|
+
|
|
271
|
+
## When to do something else
|
|
272
|
+
|
|
273
|
+
| Situation | Go to |
|
|
274
|
+
|---|---|
|
|
275
|
+
| Porting prior art (LookML, dbt, a metrics doc): the definitions exist and are agreed, the job is translation | `skill:malloy-lookml-review`, then `skill:malloy-model` |
|
|
276
|
+
| The user names the sources they want built outright, before any question | `skill:malloy-model` |
|
|
277
|
+
| A model already exists, the question rests on no judgment call, and nothing is worth keeping | `skill:malloy-analysis` alone |
|
|
278
|
+
| Open-ended exploration with no intent to keep anything | `skill:malloy-analyze` |
|
|
279
|
+
|
|
280
|
+
## Anti-patterns
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
WRONG Turn "what's our default rate?" into a modelling project
|
|
284
|
+
RIGHT Answer it, then codify what the answer assumed
|
|
285
|
+
|
|
286
|
+
WRONG Propose every dimension and measure for each table in scope
|
|
287
|
+
RIGHT Codify the two fields this question actually needed
|
|
288
|
+
|
|
289
|
+
WRONG Quietly pick one of four timestamps as "the order date"
|
|
290
|
+
RIGHT "Using created_at; shipped_at would drop 38% as nulls. OK?"
|
|
291
|
+
|
|
292
|
+
WRONG Skip codifying because you cannot edit the model
|
|
293
|
+
RIGHT Put the extend and its #(doc) in the notebook, or hand over the snippet
|
|
294
|
+
|
|
295
|
+
WRONG Codify the source-level where:, then mention it in the write-up
|
|
296
|
+
RIGHT Stop and confirm it; it scopes every question anyone asks later
|
|
297
|
+
|
|
298
|
+
WRONG The assumption lives in the chat transcript, or in a // comment
|
|
299
|
+
RIGHT #(doc) on the definition for what a consumer needs, plus a line in the notes
|
|
300
|
+
|
|
301
|
+
WRONG Leave the answered question as a transcript table and move on
|
|
302
|
+
RIGHT Save it as a view; a question worth answering is usually worth re-asking
|
|
303
|
+
|
|
304
|
+
WRONG Curate access modifiers and split one file per table to "do it properly"
|
|
305
|
+
RIGHT Skip both; they solve a schema-first problem this model does not have
|
|
306
|
+
```
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-modeling
|
|
3
3
|
description: Build semantic models with Malloy for the Malloy Publisher. Read this skill whenever the user asks about modeling data or specifically mentions Malloy.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -96,9 +96,8 @@ Publishing is out of scope for open-source v1. Self-hosters move a finished mode
|
|
|
96
96
|
|
|
97
97
|
**Two paths to a model: both produce the same fully documented result:**
|
|
98
98
|
- **Schema-first:** "Model my data" → 8-step workflow above using the relevant skills
|
|
99
|
-
- **Analysis-first:**
|
|
100
|
-
|
|
101
|
-
After analysis completes, **always recommend formalizing into a model.**
|
|
99
|
+
- **Analysis-first:** a data question arrives before any model exists → `skill:malloy-model-as-you-go`. It answers the question with `skill:malloy-analysis`, then codifies what the answer assumed into the model, one question at a time, confirming binding decisions first. The model exists by the end; there is no separate formalize step.
|
|
100
|
+
- **Open-ended exploration** with no intent to keep anything: `skill:malloy-analyze`. If it turns into something worth keeping, formalize via `skill:malloy-model` (`reference/analysis-to-model.md`).
|
|
102
101
|
|
|
103
102
|
## Agent Behavior
|
|
104
103
|
|
|
@@ -120,9 +119,9 @@ After analysis completes, **always recommend formalizing into a model.**
|
|
|
120
119
|
| "Model from LookML" | 8-step with prior art via `skill:malloy-lookml-review` |
|
|
121
120
|
| "Explore this data", "what's interesting?", "show me the top X" | `skill:malloy-analyze` (EDA) |
|
|
122
121
|
| "Build a dashboard", "create views" on existing model | `skill:malloy-analyze` (views), plus `skill:malloy-charts` or `skill:malloy-notebooks` as needed |
|
|
123
|
-
| "Build a model but not sure what metrics" | `skill:malloy-
|
|
122
|
+
| "Build a model but not sure what metrics" | `skill:malloy-model-as-you-go`: answer their first real question, codify what it assumed, repeat |
|
|
124
123
|
|
|
125
|
-
**If the user's first message is a data question** (not "build me a model"), route to `skill:malloy-
|
|
124
|
+
**If the user's first message is a data question** (not "build me a model"), route to `skill:malloy-model-as-you-go`. It answers with `skill:malloy-analysis` and grows the model from what each answer assumed, so there is nothing to formalize afterwards.
|
|
126
125
|
|
|
127
126
|
## Additional Support Skills
|
|
128
127
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-notebook-chat
|
|
3
3
|
description: Steps to follow when the chat is bound to a notebook or saved report. The notebook's cells are the agent's primary context, answer from it, run its queries, and only reach for get_context when the user asks about something outside it.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-notebooks
|
|
3
3
|
description: Create Malloy notebooks (.malloynb) for interactive dashboards and data stories. Use when user asks to "create a notebook", "make a dashboard notebook", "write a malloynb", "data story", or needs to build reports/visualizations.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-patterns
|
|
3
3
|
description: Index of Malloy documentation topics. Use to discover what's available in search_malloy_docs. Covers language reference (sources, queries, views, fields, aggregates, joins, filters, expressions, functions), common patterns (YoY, cohorts, percent of total), rendering, and experimental features.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-phrase-detection
|
|
3
3
|
description: How to construct search targets for the get_context tool. Covers target-type classification and non-obvious decomposition patterns. Read the tool description for field definitions and the end-to-end workflow.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-publish
|
|
3
3
|
description: Package Malloy models for serving by Malloy Publisher. Use when user asks to "publish", "package", "deploy", or wants to share models with others.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-queries
|
|
3
3
|
description: Malloy query patterns, syntax rules, and chart annotation reference. Consult before writing or debugging any query. Covers dates, aggregates vs dimensions, join paths, filters, string matching, and common error patterns.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-review
|
|
3
3
|
description: Malloy semantic-model code review. Invoke when the user asks to review, audit, or critique a `.malloy` file, a folder of Malloy models, or a GitHub PR that touches Malloy. Enforces project modeling standards and emits a navigable review file.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-scope
|
|
3
3
|
description: Present discovery findings and propose an analytical scope before modeling. Use after inspecting a package's model and data, to classify tables and recommend an analytical focus the user can pick from.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|