@malloy-publisher/skills 0.1.21 → 0.1.23
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/eval-answer/SKILL.md +1 -1
- package/skills/eval-answer/scripts/check_findable.py +99 -0
- package/skills/eval-answer/scripts/check_findable_test.py +38 -0
- package/skills/eval-answer/scripts/score_retrieval.py +14 -1
- package/skills/eval-answer/scripts/score_retrieval_test.py +8 -1
- package/skills/eval-answer/scripts/verify_goldens.py +44 -0
- package/skills/eval-diagnose/SKILL.md +16 -1
- package/skills/eval-import/SKILL.md +1 -1
- package/skills/eval-improve/SKILL.md +1 -1
- package/skills/eval-judge/SKILL.md +22 -1
- package/skills/eval-loop/SKILL.md +21 -6
- package/skills/eval-loop/scripts/build_run_package.py +17 -4
- package/skills/eval-loop/scripts/run_baseline.py +117 -6
- package/skills/eval-loop/scripts/run_baseline_test.py +127 -12
- package/skills/eval-loop/templates/eval-run-package/public/app.js +29 -4
- package/skills/eval-loop/templates/eval-run-package/public/index.html +35 -7
- package/skills/eval-report/SKILL.md +101 -12
- package/skills/malloy/SKILL.md +2 -1
- package/skills/malloy-analysis/SKILL.md +2 -2
- package/skills/malloy-analysis-pitfalls/SKILL.md +2 -2
- package/skills/malloy-analysis-report/SKILL.md +1 -1
- package/skills/malloy-analyze/SKILL.md +1 -1
- package/skills/malloy-charts/SKILL.md +1 -1
- package/skills/malloy-dashboards/SKILL.md +26 -8
- package/skills/malloy-debug/SKILL.md +1 -1
- package/skills/malloy-define/SKILL.md +1 -1
- package/skills/malloy-discover/SKILL.md +1 -1
- package/skills/malloy-document/SKILL.md +1 -1
- 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 +43 -24
- 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 +2 -2
- package/skills/malloy-html-data-apps/SKILL.md +1 -1
- package/skills/malloy-lookml-review/SKILL.md +1 -1
- package/skills/malloy-lookml-review/reference/curate-visibility.md +1 -1
- package/skills/malloy-materialization/SKILL.md +3 -3
- package/skills/malloy-materialization-tuning/SKILL.md +2 -2
- package/skills/malloy-model/SKILL.md +51 -17
- package/skills/malloy-model-as-you-go/SKILL.md +2 -2
- package/skills/malloy-modeling/SKILL.md +2 -2
- package/skills/malloy-notebook-chat/SKILL.md +1 -1
- package/skills/malloy-notebooks/SKILL.md +2 -2
- package/skills/malloy-patterns/SKILL.md +1 -1
- package/skills/malloy-phrase-detection/SKILL.md +14 -9
- package/skills/malloy-publish/SKILL.md +31 -12
- package/skills/malloy-queries/SKILL.md +11 -12
- package/skills/malloy-review/SKILL.md +1 -1
- package/skills/malloy-scope/SKILL.md +1 -1
- package/skills/malloy-source-unreachable/SKILL.md +71 -0
|
@@ -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.23
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -111,7 +111,7 @@ source: spotlight_carrier is duckdb.table('data/carriers.parquet') extend {
|
|
|
111
111
|
}
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
Declare givens at the **top of the model file**, before the source that uses them, not in the notebook. `malloy-model` § Access Control covers the syntax and the `#(authorize)` gating story built on top of givens.
|
|
114
|
+
Declare givens at the **top of the model file**, before the source that uses them, not in the notebook. `malloy-model` § Access Control covers the syntax and the `#(authorize)`/`#(access_filter)` gating story built on top of givens.
|
|
115
115
|
|
|
116
116
|
## Legacy: `#(filter)` annotations
|
|
117
117
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-patterns
|
|
3
3
|
description: An index of what search_malloy_docs covers - language reference, common patterns (YoY, cohorts, percent of total), rendering - for picking a topic worth searching before querying the docs.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.23
|
|
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 phrase search_text on a get_context call so retrieval returns the fields you need instead of a truncated catalog. Covers target-type classification and decomposition patterns.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.23
|
|
5
5
|
---
|
|
6
6
|
<!-- Copyright (c) Credible Data Inc. SPDX-License-Identifier: MIT -->
|
|
7
7
|
|
|
@@ -11,9 +11,9 @@ The `get_context` tool description defines each field and what a call returns. T
|
|
|
11
11
|
|
|
12
12
|
> **Tool names** are written bare here - `get_context`, `execute_query`, `search_malloy_docs`. The exact prefixed name depends on the host surface; match each against the tools you actually have.
|
|
13
13
|
|
|
14
|
-
**Scope of this skill:** the patterns below build `dimension` / `measure` / `view` targets. Phrasing for `source` targets is covered at the end.
|
|
14
|
+
**Scope of this skill:** the patterns below build `dimension` / `measure` / `view` / `dimensional_value` targets. Phrasing for `source` targets is covered at the end.
|
|
15
15
|
|
|
16
|
-
**A note on matching:** `get_context` searches over the model (sources, fields, views, and their descriptions),
|
|
16
|
+
**A note on matching:** `get_context` searches over the model (sources, fields, views, and their descriptions), and `dimensional_value` targets search the categorical *values* stored in the data. Some servers don't index values; check the tool's description. On one that doesn't, find a literal value by targeting its dimension, then querying its distinct values with `execute_query` (see "Where value search isn't available" below).
|
|
17
17
|
|
|
18
18
|
## Always send `search_text`
|
|
19
19
|
|
|
@@ -35,21 +35,26 @@ One target per concept is enough: the tool handles phrasing variants internally.
|
|
|
35
35
|
- "total revenue" becomes `"the total revenue or sales amount"`
|
|
36
36
|
- **`view`**: pre-built analysis. Include one whenever the question sounds like a canned report (summary, breakdown, top-N, trend).
|
|
37
37
|
- "sales summary" becomes `"a summary of sales metrics"`
|
|
38
|
+
- **`dimensional_value`**: a literal value the user named, stored in some dimension. `search_text` is the value itself, the one target where echoing the user's word is right.
|
|
39
|
+
- "CyberArk" stays `"CyberArk"`
|
|
40
|
+
- Best practice is to scope the call to the source that holds the value. An unscoped value search covers every indexed dimension in scope, so it is slow on a large package. If you truly don't know the source, you can leave the scope off, but expect a slow call.
|
|
38
41
|
- **`source`**: data domain, for a question that names a subject area rather than fields (phrasing below).
|
|
39
42
|
|
|
40
|
-
**Resolving categorical values
|
|
43
|
+
**Resolving categorical values.** When the user names a literal value like "premium" or "New York City", send a `dimensional_value` target for it, scoped to its source once you know the source. Filter on the exact string it returns: the data may store `"Premium"`, `"PREMIUM"`, `"NYC"`, or `"New York"`, and only the data tells you which.
|
|
44
|
+
|
|
45
|
+
**Where value search isn't available.** Some servers have no value index and return nothing for a `dimensional_value` target, and on others a particular dimension's values may not be indexed. Then target the *dimension* the value lives on (`"the subscription tier"`, `"the city where the subscriber lives"`), and confirm the exact stored string by querying that dimension's distinct values with `execute_query` before you filter on it.
|
|
41
46
|
|
|
42
47
|
## Non-obvious decomposition patterns
|
|
43
48
|
|
|
44
49
|
These are the rules you won't apply correctly by default:
|
|
45
50
|
|
|
46
|
-
1. **Adjective + noun, split.** "active users" becomes two dimension targets: one for the attribute (`"the status of the user account"`) and one for the noun (`"the user or account holder"`). Resolve the modifier ("active") to the exact stored value
|
|
51
|
+
1. **Adjective + noun, split.** "active users" becomes two dimension targets: one for the attribute (`"the status of the user account"`) and one for the noun (`"the user or account holder"`). Resolve the modifier ("active") to the exact stored value with a `dimensional_value` target, or the status dimension's distinct values where values aren't indexed.
|
|
47
52
|
2. **Ambiguous concept, cover both types.** "rating", "duration", and the like could be either a dimension or a measure: create one target of each type.
|
|
48
53
|
3. **Time references are dimensions.** "last year" becomes a dimension target for the relevant date field (`"the date the event occurred"`).
|
|
49
54
|
4. **Numeric ranges are dimensions.** "aged 50", "revenue over $1M" become dimension targets; the comparison is applied in the query, not matched as text.
|
|
50
|
-
5. **Categorical strings that look numeric are still dimensions.** "18-30", "<5 days", "tier 2" are stored as literal strings on a dimension. Target that dimension, then confirm the exact string with `execute_query
|
|
55
|
+
5. **Categorical strings that look numeric are still dimensions.** "18-30", "<5 days", "tier 2" are stored as literal strings on a dimension. Target that dimension, then confirm the exact string with a `dimensional_value` target, or `execute_query` where values aren't indexed.
|
|
51
56
|
6. **"Top N" without a named measure, add a ranking measure.** "top 6 products" becomes a measure for the ranking concept (`"the performance metric for a product"`) plus a dimension for the entity. If the measure is explicit ("top products by total sales"), use it directly and skip the generic ranking measure.
|
|
52
|
-
7. **Multiple values for one concept, one dimension target.** Several values ("premium and basic") still map to a single dimension target for the parent field;
|
|
57
|
+
7. **Multiple values for one concept, one dimension target.** Several values ("premium and basic") still map to a single dimension target for the parent field; confirm the exact stored values with `dimensional_value` targets, or `execute_query` where values aren't indexed.
|
|
53
58
|
8. **The quantity asked for is a measure, even when nothing names it.** "How many titles were released in 2019?" is asking for a count, but no noun in it is the count: the visible phrases are the subject ("titles") and the filter ("2019"), and mapping only those yields a `source` and a `dimension` target with no way to answer. Always add a measure target for the quantity itself (`"the number of titles"`). This holds for every interrogative that IS the aggregation -- "how many", "how much", "how often" -- and it is the pattern most easily lost when a filter or grouping is the loud part of the sentence. Measured: an answerer got this right on "How many titles are in the dataset?" and dropped it on "How many titles were released in 2019?", where the year took the attention.
|
|
54
59
|
9. **A population qualifier is a target, and so is the one the question omits.** Words like "real", "actual", "genuine", "live" or "production" are not filler: they name rows the model marks for exclusion. Target the flag itself (`"the flag marking synthetic, test or monitoring traffic"`), not just the noun they modify. Add one such target even when the question carries no qualifier at all, because a table of events, requests, sessions or logs usually holds test, internal or cancelled rows and nothing in the wording will say so. Resolve it to the model's own flag rather than inventing a filter on an id or a name; a hand-rolled exclusion and the documented one rarely select the same rows.
|
|
55
60
|
|
|
@@ -67,9 +72,9 @@ The targets for this question:
|
|
|
67
72
|
| `dimension` | `"the tier of the subscription"` |
|
|
68
73
|
| `view` | `"subscriber churn or retention analysis"` |
|
|
69
74
|
|
|
70
|
-
Key moves: time ("last year") becomes a dimension on the cancellation date; "NYC" and "premium/basic subscribers"
|
|
75
|
+
Key moves: time ("last year") becomes a dimension on the cancellation date; "NYC" and "premium/basic subscribers" resolve to the city and tier dimensions in this first call, because the source is not known yet and an unscoped value search is slow. One `view` target is included to surface any canned churn analysis.
|
|
71
76
|
|
|
72
|
-
The response returns the source these fields live on (here `subscriptions`) with the matched fields on its card. Then
|
|
77
|
+
The response returns the source these fields live on (here `subscriptions`) with the matched fields on its card. Then send a second call scoped to `subscriptions`, with `dimensional_value` targets for `"NYC"`, `"premium"` and `"basic"`, to get the exact strings to filter on ("New York City" vs "NYC", "premium" vs "Premium"). Where values aren't indexed, run `execute_query` on the city and tier dimensions' distinct values instead.
|
|
73
78
|
|
|
74
79
|
## Authoring `search_text` for `source` targets
|
|
75
80
|
|
|
@@ -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.23
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -56,28 +56,47 @@ If it doesn't exist, create one. Suggest a package name based on the model conte
|
|
|
56
56
|
|
|
57
57
|
### Curating discovery & the query boundary (optional)
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
A package with no `index.malloy` and no `explores` exposes **everything**: every model is listed and every source is directly queryable.
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
To curate, add an **`index.malloy`** at the package root. Publisher reads it as the package's published surface, so no manifest field is involved:
|
|
62
|
+
|
|
63
|
+
```malloy
|
|
64
|
+
// index.malloy
|
|
65
|
+
import "order_analysis.malloy"
|
|
66
|
+
import "staging.malloy"
|
|
67
|
+
|
|
68
|
+
export { orders, customers }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
What it exports is what agents discover **and** what may be queried. Everything else still compiles, and other models can import, join and extend it, but a direct query against it is refused with a 404 - indistinguishable from a source that does not exist. Reach for this when you have raw/staging/scaffolding sources that exist to build a curated entry point and you don't want agents landing on, or querying, them directly.
|
|
72
|
+
|
|
73
|
+
**Address queries to the surface.** Once a package has an `index.malloy`, `.../models/staging.malloy/query` is no longer a query entry point, *even for a source that file declares itself*. Use `.../models/index.malloy/query`. If you are debugging a refusal rather than authoring, `skill:malloy-source-unreachable` covers the three ways a source can be out of reach and how to tell them apart.
|
|
74
|
+
|
|
75
|
+
**A surface can be layered.** An `index.malloy` may front a file that fronts another. A source re-exported through a chain of files stays queryable through the surface at any depth, because admission follows the declaration rather than the path taken to it.
|
|
76
|
+
|
|
77
|
+
**Curation hides a landing point, not a column.** A published source may `join` an unpublished one, and a query grouping by a joined field returns that field's values normally. If a column must not be readable, do not join it into something you publish; gate it with `#(authorize)` instead.
|
|
78
|
+
|
|
79
|
+
**Leaving a source out does not put it out of reach, but there is a condition.** `export { ... }` also decides what an *importing* file may see, which is Malloy's rule rather than Publisher's. A file that declares no `export` hands an importer everything it declares, so an unpublished source stays importable and joinable from the file that declares it. A file that *does* declare one hands over exactly that list: importing a file whose `export` omits a source and then naming it fails to compile with `Reference to undefined object`. Put an `export` on a mid-layer file only when you mean to narrow what its importers can build on, not just what Publisher lists.
|
|
80
|
+
|
|
81
|
+
**About `export { … }`:** the surface filters which *files* are listed; `export { … }` (a Malloy statement) filters which *sources within a file* are exposed, and the two compose. You usually don't write it in a leaf model: a file with **no** `export` exposes all of its own top-level sources. It must appear after the definitions it names. See [Malloy: Imports & Exports](https://docs.malloydata.dev/documentation/language/imports).
|
|
82
|
+
|
|
83
|
+
### The older manifest fields
|
|
84
|
+
|
|
85
|
+
Both still work and are not going away in this release. Both are deprecated where `index.malloy` replaces them, and a package using them that way gets a load-time warning naming the replacement. The two uses it cannot replace, an `explores` naming several files and `queryableSources: "all"`, stay supported without a warning. Do not add either key to a new package that does not need one of those two.
|
|
62
86
|
|
|
63
87
|
```json
|
|
64
88
|
{
|
|
65
89
|
"name": "ecommerce",
|
|
66
|
-
"version": "0.0.1",
|
|
67
|
-
"description": "Orders, customers, and revenue analysis",
|
|
68
90
|
"explores": ["order_analysis.malloy", "customer_health.malloy"],
|
|
69
|
-
"queryableSources": "
|
|
91
|
+
"queryableSources": "all"
|
|
70
92
|
}
|
|
71
93
|
```
|
|
72
94
|
|
|
73
|
-
- **`explores`** (`string[]`) -
|
|
74
|
-
- **`queryableSources`** (`"declared"` | `"all"`, default `"declared"`) - the query boundary.
|
|
75
|
-
|
|
76
|
-
**About `export { … }`:** `explores` filters which *files* are listed; `export { … }` (a Malloy statement) filters which *sources within a file* are exposed, the two compose. You usually don't write it: a file with **no** `export` exposes all of its own top-level sources. Add `export { orders, customers }` to a file to expose only those and keep imported/scaffolding helpers out of discovery (it must appear after the definitions it names). See [Malloy: Imports & Exports](https://docs.malloydata.dev/documentation/language/imports).
|
|
95
|
+
- **`explores`** (`string[]`) - model file paths relative to the package root, naming the surface. Reach for it for the one thing `index.malloy` cannot express: a surface spanning **several** files. An explicit `explores` always wins over the convention, and a package with both an `index.malloy` and an `explores` that omits it carries a warning saying so. An entry that doesn't resolve to a real `.malloy` file surfaces in `exploresWarnings`; publishing a package that has any is rejected, so fix the path before publishing. An explicit `"explores": []` means "do not curate" and suppresses the convention.
|
|
96
|
+
- **`queryableSources`** (`"declared"` | `"all"`, default `"declared"`) - the query boundary. `"declared"` is already the default, so the key changes nothing. **`"all"` is the exception, and `index.malloy` does not replace it:** it curates listings while leaving every source queryable by name, and a surface derived from an `index.malloy` always enforces the boundary. If you want listings-only curation, keep both keys.
|
|
77
97
|
|
|
78
|
-
**Why curate here:** declaring `explores` routes agents to the well-documented curated sources instead of raw tables, and `queryableSources: "declared"` keeps them from reaching the hidden sources by name. The two axes compose: list a file in `explores` for its models to be discoverable, and `export` a source within that file for it to be a landing point.
|
|
79
98
|
|
|
80
|
-
> **Not access control.**
|
|
99
|
+
> **Not access control.** The surface gates the query surface (the query endpoints, REST and MCP alike), not compile and not raw file retrieval by exact path: `/compile` and `compile_model` are deliberately exempt, because compile is the authoring loop and the boundary is discovery curation. It doesn't restrict *who* may query, only *what* is queryable by name. Queryable sources are the union of every listed file's `export {}` closure, whichever listed model path a query addresses them through. To gate access by caller-supplied identity/role, use `#(authorize)` on the source (and `#(access_filter)` to scope rows), see `skill:malloy-model` § Access Control and `docs/authorize.md`. Discovery curation and these gates are independent layers.
|
|
81
100
|
|
|
82
101
|
The manifest also carries a `scope` field (`"package"` | `"version"`, default `"package"`) controlling whether persisted/materialized artifacts are shared across published versions or owned by a single version, and a `materialization` field configuring that persistence policy (a cron `schedule` or a `freshness` window). Both are unrelated to discovery curation; there is no per-source `sharing` or `schedule` field, that was retired in favor of the single package-level `scope` and `materialization`.
|
|
83
102
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: malloy-queries
|
|
3
3
|
description: Malloy query patterns and syntax, for writing or debugging a query - dates, aggregates vs dimensions, join paths, filters, string matching, chart annotations, and the errors a SQL habit produces.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.23
|
|
5
5
|
---
|
|
6
6
|
<!--
|
|
7
7
|
Copyright (c) Credible Data Inc.
|
|
@@ -87,10 +87,10 @@ Wrong: `pick('Premium') { ... }` (that's not Malloy syntax).
|
|
|
87
87
|
**Window functions with `calculate:`:** running totals, `lag()`, `lead()`, and other window operations belong in `calculate:`, not `aggregate:`.
|
|
88
88
|
```malloy
|
|
89
89
|
run: source -> {
|
|
90
|
-
group_by:
|
|
90
|
+
group_by: order_month is order_date.month
|
|
91
91
|
aggregate: revenue
|
|
92
92
|
calculate: prev_month_revenue is lag(revenue)
|
|
93
|
-
order_by:
|
|
93
|
+
order_by: order_month
|
|
94
94
|
}
|
|
95
95
|
```
|
|
96
96
|
|
|
@@ -128,15 +128,15 @@ where: order_date >= @2020-01-01
|
|
|
128
128
|
|
|
129
129
|
### Truncation accessors return timestamps, not integers
|
|
130
130
|
|
|
131
|
-
`order_date.month` returns a month-truncated timestamp (e.g., `2025-06-01 00:00:00`), useful for `group_by:`. It is NOT a 1-12 integer
|
|
131
|
+
`order_date.month` returns a month-truncated timestamp (e.g., `2025-06-01 00:00:00`), useful for `group_by:`. It is NOT a 1-12 integer.
|
|
132
132
|
|
|
133
|
-
Available truncations: `.year`, `.quarter`, `.month`, `.week`, `.day`, `.hour`, `.minute`, `.second`.
|
|
133
|
+
Available truncations: `.year`, `.quarter`, `.month`, `.week`, `.day`, `.hour`, `.minute`, `.second`.
|
|
134
134
|
|
|
135
|
-
### `month`, `year`, `day`, `quarter` are reserved
|
|
135
|
+
### `month`, `year`, `day`, `quarter` are reserved as names
|
|
136
136
|
|
|
137
|
-
|
|
137
|
+
They cannot name an output field. The functions are fine: `month(order_date)` extracts 1-12 anywhere, including `where:`.
|
|
138
138
|
|
|
139
|
-
Wrong: `group_by: month is order_date.month` →
|
|
139
|
+
Wrong: `group_by: month is order_date.month` → `'month' is a reserved word, so to use it as a name you must quote it`
|
|
140
140
|
Right: `group_by: order_month is order_date.month`
|
|
141
141
|
Right (when a column is literally named `month`): `` group_by: `month` ``
|
|
142
142
|
|
|
@@ -252,7 +252,7 @@ run: source -> {
|
|
|
252
252
|
}
|
|
253
253
|
```
|
|
254
254
|
|
|
255
|
-
Charts render only the **first** aggregate. For multiple measures on one chart, place `# y` above the `aggregate:` keyword or use the `y=['a','b']` shorthand. A
|
|
255
|
+
Charts render only the **first** aggregate. For multiple measures on one chart, place `# y` above the `aggregate:` keyword or use the `y=['a','b']` shorthand. A chart annotation left as the last line inside `{ }` fails with *"Parser enountered unexpected statement type 'unimplemented'"* (the compiler's spelling); one after the closing `}` fails with *"Object annotation not connected to any object"*.
|
|
256
256
|
|
|
257
257
|
Read the `malloy-charts` skill for chart types, properties, data shape requirements, and selection guidance.
|
|
258
258
|
|
|
@@ -263,11 +263,10 @@ Read the error against the tables above and below. Most failures match a known p
|
|
|
263
263
|
| Error message | Likely cause / fix |
|
|
264
264
|
|---|---|
|
|
265
265
|
| `Cannot compare a timestamp to a number` | Comparing `date.year` to an integer. Use `date >= @2020-01-01` instead. |
|
|
266
|
-
| `no viable alternative at input '<word>'` |
|
|
266
|
+
| `no viable alternative at input '<word>'` | Often a `;` between fields within one clause - fields under one `aggregate:`/`group_by:` are comma- or newline-separated (the error points at the field right after the `;`). |
|
|
267
267
|
| `'<field_name>' is not defined` | Field doesn't exist in the source. Re-check against the model definition; you may have stripped a join prefix. |
|
|
268
|
-
| `missing {DAY, HOUR, MINUTE, MONTH, QUARTER, SECOND, WEEK, YEAR}` | Chained date property too deep (e.g., `.month.something`). Stop at the first truncation. |
|
|
269
268
|
| `field is a bar chart, but is not a repeated record` | Chart annotation placed inside `{ }`. Move `# bar_chart` above `run:` / `view:` / `nest:`. |
|
|
270
|
-
| `Parser
|
|
269
|
+
| `Parser enountered unexpected statement` | Spelled that way by the compiler. Most often a chart annotation left as the last line inside `{ }` - move it above `run:`. Also syntax Malloy doesn't allow in that position (e.g., `pick` inside a nested view). |
|
|
271
270
|
| Query silently returns zero rows | Filter value mismatch (case, spelling, format). Run a distinct-values query on the dimension to confirm the literal. |
|
|
272
271
|
|
|
273
272
|
## Syntax Help
|
|
@@ -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.23
|
|
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.23
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!--
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: malloy-source-unreachable
|
|
3
|
+
description: Work out why a source is missing from get_context, or why a query against it was refused with a 404 or a 403, and what to do next. Use when a source you expect is absent from discovery, when execute_query answers "No queryable source", when a query that used to work stops working, or when deciding whether a package's curation is hiding something you need.
|
|
4
|
+
version: 0.1.23
|
|
5
|
+
---
|
|
6
|
+
<!-- Copyright (c) Credible Data Inc. SPDX-License-Identifier: MIT -->
|
|
7
|
+
|
|
8
|
+
# A source you cannot reach
|
|
9
|
+
|
|
10
|
+
> **Tool names** are written bare here - `get_context`, `execute_query`, `get_status`, `compile_model`. The exact prefixed name depends on the host surface; match each against the tools you actually have.
|
|
11
|
+
|
|
12
|
+
Most packages publish a curated surface, so a source being absent or refused is usually the package working as its author intended, not a fault. Read the refusal before working around it, because the three things that can refuse you need three different responses and they are told apart by the status code.
|
|
13
|
+
|
|
14
|
+
## The three answers
|
|
15
|
+
|
|
16
|
+
| what you got | what it means | what to do |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| **404**, "No queryable source", "No queryable model" or "Query target is not queryable" | the source is not on the package's published surface, or does not exist. The two are deliberately indistinguishable so that a refusal cannot be used to probe for hidden names | query something on the surface, or ask the package's author to publish it |
|
|
19
|
+
| **403**, "Access denied" | the source exists and is on the surface, but an `#(authorize)` gate did not admit you | supply the givens the gate reads, or accept that this caller may not read it |
|
|
20
|
+
| **200** with zero rows | you were admitted, and an `#(access_filter)` narrowed the rows to none of them | this is a real answer. Report it as "no matching rows", never as an error |
|
|
21
|
+
|
|
22
|
+
A 403 names a source, so it tells you the source exists. A 404 tells you nothing at all. That asymmetry is deliberate: a source that is both hidden AND gated answers **404**, so you can never use a 403 to discover that a hidden name is real.
|
|
23
|
+
|
|
24
|
+
## Addressing the surface, which is the most common 404
|
|
25
|
+
|
|
26
|
+
A query names a model file. When a package curates its surface, only files on that surface are valid entry points, **even for a source that file declares itself**:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
POST .../packages/sales/models/index.malloy/query {"sourceName": "orders"} -> 200
|
|
30
|
+
POST .../packages/sales/models/orders.malloy/query {"sourceName": "orders"} -> 404
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Same source, same package. `orders.malloy` is off the surface, so it is not an entry point.
|
|
34
|
+
|
|
35
|
+
**Use the `model_path` that `get_context` gave you, verbatim.** Its `source_info.resource_id` holds `environment`, `package`, `model_path` and `source`, and those are exactly `execute_query`'s `environmentName`, `packageName`, `modelPath` and `sourceName`. On a curated package the `model_path` is the surface file, not the file that declares the source. Substituting the declaring file because it looks more correct is how this 404 happens.
|
|
36
|
+
|
|
37
|
+
## When the source is not in `get_context` at all
|
|
38
|
+
|
|
39
|
+
Work down this list. The first three are far more common than the last.
|
|
40
|
+
|
|
41
|
+
1. **It is off the surface.** The package publishes a curated set and this source is not in it. Nothing you can do from the query side; it is a package edit.
|
|
42
|
+
2. **The package failed to load, or is serving a stale model.** Call `get_status`. A package that never loaded is absent from listings entirely, which reads exactly like "does not exist". A package whose last reload failed to compile is listed and answering, from the model it compiled *before* that save, and carries `stale: true`. Neither is visible from a listing alone.
|
|
43
|
+
3. **You are talking to a different server than you think.** Call `list_packages` and check the environment and package names are the ones you expect. A stale `.mcp.json` outlives the server that wrote it, and another Publisher may hold that port.
|
|
44
|
+
4. **Your search phrasing missed it.** Retry with a bare target (no `search_text`) to enumerate rather than rank. If it appears there, the source exists and was a ranking miss.
|
|
45
|
+
|
|
46
|
+
## What curation does not do
|
|
47
|
+
|
|
48
|
+
- **It does not hide fields.** A published source may `join` an unpublished one, and a query grouping by a joined field returns that field's values normally. Hiding a source removes it as a landing point; it does not redact columns a published source pulls in.
|
|
49
|
+
- **It does not gate `/compile`.** `compile_model` is exempt, because compile is the authoring loop. A hidden source can still be compile-checked, and that is intended. The exception is a hidden source that is also gated, which answers 404 at compile too.
|
|
50
|
+
- **It is not access control.** Curation answers "what is queryable by name", not "who may query it". Identity is `#(authorize)` and `#(access_filter)`. A source is not protected by being hidden.
|
|
51
|
+
|
|
52
|
+
## Reaching an unpublished source from your own model
|
|
53
|
+
|
|
54
|
+
If you are writing Malloy rather than just querying, an unpublished source is still usable, with one condition that trips people.
|
|
55
|
+
|
|
56
|
+
`export { ... }` decides what an **importing file** can see, which is Malloy's rule and not Publisher's:
|
|
57
|
+
|
|
58
|
+
- a file that declares **no** `export` hands an importer everything it declares, so import the file that declares the source and join it normally;
|
|
59
|
+
- a file that **does** declare one hands over exactly that list. Importing a file whose `export` omits the source and then naming it fails to compile with `Reference to undefined object`.
|
|
60
|
+
|
|
61
|
+
So an unpublished source is reachable through a file that exports it, or that exports nothing, and not through a file whose `export` leaves it out. Re-export chains are fine at any depth: a source re-exported through several files stays queryable through the surface, because admission follows the declaration rather than the path taken to it.
|
|
62
|
+
|
|
63
|
+
## Before you report a source as missing
|
|
64
|
+
|
|
65
|
+
Confirm all three, in this order. Each one has been mistaken for "the data is not there".
|
|
66
|
+
|
|
67
|
+
1. `get_status` shows the package loaded and not `stale`.
|
|
68
|
+
2. `list_packages` names the environment and package you meant.
|
|
69
|
+
3. An enumerating `get_context` call (a target with no `search_text`) does not list it.
|
|
70
|
+
|
|
71
|
+
Only then is it genuinely not published, and the answer to the user is that the package does not expose it, not that the data does not exist.
|