@malloy-publisher/skills 0.1.51 → 0.1.52

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.
Files changed (37) hide show
  1. package/package.json +1 -1
  2. package/skills/eval-answer/SKILL.md +1 -1
  3. package/skills/eval-diagnose/SKILL.md +1 -1
  4. package/skills/eval-import/SKILL.md +1 -1
  5. package/skills/eval-improve/SKILL.md +1 -1
  6. package/skills/eval-judge/SKILL.md +1 -1
  7. package/skills/eval-loop/SKILL.md +1 -1
  8. package/skills/eval-report/SKILL.md +1 -1
  9. package/skills/malloy-analysis/SKILL.md +2 -1
  10. package/skills/malloy-analysis-report/SKILL.md +1 -1
  11. package/skills/malloy-charts/SKILL.md +1 -1
  12. package/skills/malloy-classify/SKILL.md +1 -1
  13. package/skills/malloy-compliance-review/SKILL.md +1 -1
  14. package/skills/malloy-dashboards/SKILL.md +1 -1
  15. package/skills/malloy-define/SKILL.md +1 -1
  16. package/skills/malloy-define-agent/SKILL.md +1 -1
  17. package/skills/malloy-discover/SKILL.md +1 -1
  18. package/skills/malloy-document/SKILL.md +1 -1
  19. package/skills/malloy-getting-started/SKILL.md +2 -2
  20. package/skills/malloy-gotchas-modeling/SKILL.md +2 -2
  21. package/skills/malloy-html-data-apps/SKILL.md +1 -1
  22. package/skills/malloy-lookml-review/SKILL.md +1 -1
  23. package/skills/malloy-lookml-review/reference/build-unnest.md +1 -1
  24. package/skills/malloy-materialization/SKILL.md +1 -1
  25. package/skills/malloy-model/SKILL.md +1 -1
  26. package/skills/malloy-model-as-you-go/SKILL.md +1 -1
  27. package/skills/malloy-modeling/SKILL.md +1 -1
  28. package/skills/malloy-notebooks/SKILL.md +1 -1
  29. package/skills/malloy-phrase-detection/SKILL.md +1 -1
  30. package/skills/malloy-powerbi-review/SKILL.md +1 -1
  31. package/skills/malloy-publish/SKILL.md +1 -1
  32. package/skills/malloy-queries/SKILL.md +23 -1
  33. package/skills/malloy-reports/SKILL.md +1 -1
  34. package/skills/malloy-review/SKILL.md +1 -1
  35. package/skills/malloy-review/reference/rubric-correctness.md +1 -1
  36. package/skills/malloy-run-agent/SKILL.md +1 -1
  37. package/skills/malloy-source-unreachable/SKILL.md +1 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@malloy-publisher/skills",
3
3
  "description": "Agent skills for Malloy and the Malloy Publisher",
4
- "version": "0.1.51",
4
+ "version": "0.1.52",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: eval-answer
3
3
  description: 'Score one analytical answer against a verified golden, and score which of the entities the golden depends on retrieval delivered to the answerer. Run the contamination checklist, re-execute the submitted query yourself, then spawn a judge subagent per skill:eval-judge. Append attempt, tool_call, score, and retrieval_score events to the file ledger (reference/ledger-schema.md). Never explain the failure (eval-diagnose) or edit the model (eval-improve). Use when asked whether an answer was correct, to score a run, or to baseline a model.'
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  # Evaluate One Answer
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: eval-diagnose
3
3
  description: 'Diagnose why a scored answer failed and who owns the fix, then cluster the failures by shared root cause. Walk dataset, agent-call, get_context/model, get_context/retrieval, construction, then model-definition. Append issue events to the file ledger, linked by traceId, one per cluster. Use after eval-answer, when triaging a run, or before changing a model. Does not edit the model (eval-improve).'
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  # Diagnose One Answer
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: eval-import
3
3
  description: 'Turn a list of questions into an eval set, whatever shape it arrived in: a JSONL a customer sent, a CSV, a spreadsheet export, a markdown doc, an email thread, or a pull from production logs. Classify each item by what came WITH the question (a query, a number, prose criteria, or nothing), write cases.jsonl and set.json per reference/case-format.md, keep the file as it arrived, and seal each question so a later edit is detectable. Never marks a value-bearing golden verified and never invents a question. Use when questions arrive from outside and need to become a set, or before the first run against a set nobody here authored.'
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  # Import questions into an eval set
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: eval-improve
3
3
  description: 'Make the smallest safe edit that closes a diagnosed gap the model repo owns: a Malloy model edit, or a package skill under skills/. Every factual claim carries a probe receipt. Use after eval-diagnose, or when asked to fix a model so an agent can discover the right answer. Never accepts its own edit; the acceptance check belongs to eval-loop. Does not decide whether an answer was wrong (eval-answer) or why (eval-diagnose).'
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  # Improve the Model
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: eval-judge
3
3
  description: 'Decide whether ONE answer matches its golden, and say whether you believe the golden. Read this before emitting any verdict. Covers containment, column pairing, near_match, refusals, and the gold_status judgement. Use when scoring an attempt in an evaluation run; never to conduct a run (eval-loop), diagnose a failure (eval-diagnose) or edit a model (eval-improve).'
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  # The judge
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: eval-loop
3
3
  description: 'Conduct a local Publisher evaluation loop in five steps: scrape/run, eval, diagnose, improve, checkpoint. You are the conductor: import cases into the file ledger, spawn a blind answerer, then run eval-answer, eval-diagnose, and eval-improve. Persistence is plain files: the set in the model package''s evals/ directory, runs in the set''s workdir; checkpoints are git commits of the model repo. `eval.py` runs each step from the set''s eval.toml. Use to score a model, diagnose failures, improve behind an acceptance check, or roll back a bad direction.'
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  # The Evaluation Loop
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: eval-report
3
3
  description: 'Write the summary a person reads after an evaluation run: what ran, what it scored, which failures were the MODEL and which were the EVAL itself (out of turns, contaminated, unestablished golden, unreadable judge), what was learned, and what to do next. Every claim links to the artifact behind it. Use at the end of any run, before quoting a number to anyone. Never scores an answer (eval-answer) or decides who owns a failure (eval-diagnose).'
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  # Report a Run
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: malloy-analysis
3
3
  description: "Read before your first get_context call on any business or data question. The required workflow, not background: which source, which rows, how to verify. Skipping it causes confident wrong answers."
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  <!-- Copyright (c) Credible Data Inc. SPDX-License-Identifier: MIT -->
@@ -83,6 +83,7 @@ Your first result is a draft, not an answer. The difference between a useful ana
83
83
  - **Ask "what would make this wrong?"** then run the query that would expose that problem. A plausible-looking wrong answer is the most dangerous kind.
84
84
  - **Check the common failure modes:**
85
85
  - Fan-out / double-counting: if you joined across grain, compare `count()` with `count(base_key)`, the base source's own key (in Malloy `count(field)` is already the distinct count). A large gap means base rows are repeated and aggregates are inflated. The check reads one way only: `count(joined.field)` is a distinct count at the joined grain, so it is normally much SMALLER than `count()`, which is not missing data. Measure coverage at the base grain: `count() { where: joined.field is null }`.
86
+ - Grouping by an array: a field with `relationship: "many"` that is not reached through a join (`lines.sku`, `tags.each`) gives one group per distinct element value, and a row lands in every group its array holds, so its groups add up to more than the total, as across a one-to-many join. Do not print a total row across it, and count elements with `tags.count()`, not `count(tags.each)`, which is the distinct count.
86
87
  - Broken filters: a quick count confirms a filter narrowed the data as expected. Watch case, spelling and date-format mismatches; a filter that matches nothing still returns a result, just the wrong one. Confirm which dimension the user's word ("brand") maps to when several fields look alike, and that the field means what you assume (gross against net revenue): read its doc. Before a `pick` or `where` that groups values into categories, list the field's distinct values, since an unlisted one falls into `else` unnoticed. Apply the filter the question implies ("last quarter" with no time filter returns all-time data). A view can have a `where:` built in (`recent_orders`): read its code, and query the base source with explicit filters if it conflicts with the ask.
87
88
  - Null-driven loss: `count() { where: the_field is null }` shows how many rows a key field drops (`count(the_field)` counts distinct values, so subtracting it from `count()` is not a null count).
88
89
  - Parts that do not sum to the whole: if you split a total into categories, confirm they add up. If a doc says the parts are not additive (one event logged on two layers, a non-additive measure), do not print a total row across that dimension, least of all one nobody asked for; give the per-category figures and say why.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: malloy-analysis-report
3
3
  description: Combine validated Malloy queries into a report (a linear report, read top to bottom). Use when the user asks to "create a report", "combine these into a report", or wants a persistent multi-query artifact.
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  <!--
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: malloy-charts
3
3
  description: Read before choosing a chart or adding a chart annotation. Chart types, tag syntax and scale rules, KPI cards, dashboards, sparklines, and the renderer mistakes that fail silently.
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
  <!--
7
7
  Copyright (c) Credible Data Inc.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: malloy-classify
3
3
  description: 'Propose #(classification) annotations for a Malloy model from an access policy''s class scheme, backed by aggregate value evidence from the served data, and show them as a diff before writing anything. Use when the user asks to "classify the model", "tag PHI/PII", "label sensitive fields", or adds fields to a package that a policy governs, and before running malloy-compliance-review on a model nobody has classified.'
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  <!--
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: malloy-compliance-review
3
3
  description: 'Check a Malloy package and its controls against a written access policy: turn the policy''s prose into a control register, run compliance.py check against a served package (structure, access as each role, and the values themselves), judge what needs a person, and produce a report and a signable attestation. Use when the user asks whether a model "complies with", "satisfies" or "meets" a policy, HIPAA, SOC 2, PCI or the like, asks for a compliance review or attestation, or wants the review in CI.'
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  <!--
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: malloy-dashboards
3
3
  description: "Renamed to malloy-reports. A dashboard is now a report with layout=grid: a .malloy file with an ## artifact tag, written at the package root. Load malloy-reports instead when the user asks for a dashboard, a filterable view, or drill-through between views."
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  <!--
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: malloy-define
3
3
  description: Propose scope, a source plan and field definitions for a Malloy model. Which tables and questions, which sources at what grain, then renames, dimensions and measures, each backed by querying data.
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
  <!--
7
7
  Copyright (c) Credible Data Inc.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: malloy-define-agent
3
3
  description: Define a package agent - a named brief, with its own skills, optional schedules and a run budget, declared in a package's publisher.json. Use when the user asks to create, add, change or fix an agent in a package, or wants a recurring or unattended job. Not for adopting an agent that already exists (that is malloy-run-agent), and not for a one-off question, which you just answer.
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
  <!--
7
7
  Copyright (c) Credible Data Inc.
@@ -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.51
4
+ version: 0.1.52
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 runtime filters, declare given: parameters (see the malloy-model skill); #(filter) is deprecated. Filters are a runtime/modeling construct (governance, latency, correctness), not a documentation tag.'
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  <!--
@@ -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. Also use for "malloy help" or which Malloy skill to read. Covers verifying the server, discovering data with get_context, and running a first grounded query.
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
  <!--
7
7
  Copyright (c) Credible Data Inc.
@@ -106,7 +106,7 @@ Two tools, in this order.
106
106
 
107
107
  A search takes exactly one `scopes` entry, because retrieval is indexed per package. With no `search_targets`, `get_context` lists instead: no arguments lists the environments, `environment` lists that environment's packages, a package in `scopes` lists its sources, and a source lists its fields. A level with only one choice is entered, so on a server with one package a call with no arguments returns its sources.
108
108
 
109
- Use the names it returns exactly. Do not invent environments, packages, sources, or fields. A field reached through a join is named by its full dotted path; pass it verbatim rather than rebuilding it.
109
+ Use the names it returns exactly. Do not invent environments, packages, sources, or fields. A field reached through a join is named by its full dotted path; pass it verbatim rather than rebuilding it. A member of a record column is also named by its dotted path (`delivery.city`), and is not a join. A member of a repeated record has `relationship: "many"`: grouping by it gives a group per element value, and the groups can add up to more than the total. An array of plain values is listed as `tags.each`, its values, also `many`.
110
110
 
111
111
  ## 2. Run the query
112
112
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: malloy-gotchas-modeling
3
3
  description: Malloy modeling mistakes and compile-error fixes. Read before writing sources, dimensions, measures or joins, and when a .malloy file will not compile. Reserved words, NULLs, dates, field access.
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
  <!--
7
7
  Copyright (c) Credible Data Inc.
@@ -290,7 +290,7 @@ source: facts is conn.table('orders') -> { group_by: user_id, aggregate: total i
290
290
  | Looks like it needs SQL | Malloy equivalent |
291
291
  |---|---|
292
292
  | Multi-CTE pipeline | Stacked query-based sources: `source: a is t -> {...}`; `source: b is a -> {...}`; `source: c is b -> {...}` |
293
- | UNNEST / array column access | An array of records is read by its field path: `group_by: ys.yr`, `aggregate: tv is ys.v.sum()`. An array of plain values is read with `.each` and nothing after it: `group_by: v is arr.each`. `arr.each.field` does not exist ([data types docs](https://docs.malloydata.dev/documentation/language/datatypes#array-access)) |
293
+ | UNNEST / array column access | An array of records is read by its field path: `group_by: ys.yr`, `aggregate: tv is ys.v.sum()`. An array of plain values is read with `.each` and nothing after it: `group_by: v is arr.each`. `arr.each.field` does not exist. Counting: `count(arr.each)` is the distinct count, `arr.count()` the element count, and `arr.each.count()` does not compile ([data types docs](https://docs.malloydata.dev/documentation/language/datatypes#array-access)) |
294
294
  | PIVOT (conditional aggregation) | Filtered aggregates: `aggregate: a is x.sum() { where: cat = 'a' }, b is x.sum() { where: cat = 'b' }` |
295
295
  | Window functions (any frame, including custom) | `calculate:` with `sum_cumulative`, `lag`, `lead`, `rank`, `row_number`, `avg_moving`, `first_value`, `last_value`: supports `partition_by:` and `order_by:` ([window functions docs](https://docs.malloydata.dev/documentation/language/functions#window-functions)) |
296
296
  | `ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING` | `sum_cumulative(x) - x` (cumulative-including-current minus current = cumulative-excluding-current) |
@@ -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): design it, write its Publisher.query code, embed it. Hand-authored, no build step."
4
- version: 0.1.51
4
+ version: 0.1.52
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.51
4
+ version: 0.1.52
5
5
  ---
6
6
  <!--
7
7
  Copyright (c) Credible Data Inc.
@@ -23,7 +23,7 @@ Malloy handles nested structures natively. The conversion depends on whether you
23
23
  1. **Keep nested**: access nested fields directly via the parent source
24
24
  2. **Flatten**: create a separate source from the unnested data
25
25
 
26
- Call `search_malloy_docs("nested repeated")` for current Malloy syntax for nested/repeated field handling.
26
+ A repeated column is already an array on the Malloy source, so the UNNEST join usually needs no Malloy join at all. An array of records is read by its member path (`labels.key`, `credits.amount.sum()`); an array of plain values is read with `.each` (`group_by: label is labels.each`). Either fans out like the `one_to_many` join it replaces. Call `search_malloy_docs("nested repeated")` for anything beyond that.
27
27
 
28
28
  ## Struct Field Access
29
29
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: malloy-materialization
3
3
  description: Add, confirm and debug Malloy Persistence materializations in a package - persist an expensive source so queries read a pre-built table, in the source's own warehouse or in a storage destination. Read this whenever the user wants to materialize a source, add a persist annotation, speed up a slow source, build one stored rollup over another, tune what to persist, or asks why a persist source isn't building or isn't being read.
4
- version: 0.1.51
4
+ version: 0.1.52
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.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  <!--
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: malloy-model-as-you-go
3
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.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  <!--
@@ -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.51
4
+ version: 0.1.52
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: "Renamed to malloy-reports. A notebook is now a report with layout=linear: a .malloy file with an ## artifact tag, written at the package root. Load malloy-reports instead when the user asks for a notebook, a data story, or a report that reads top to bottom."
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  <!--
@@ -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.51
4
+ version: 0.1.52
5
5
  ---
6
6
  <!-- Copyright (c) Credible Data Inc. SPDX-License-Identifier: MIT -->
7
7
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: malloy-powerbi-review
3
3
  description: Move a Power BI semantic model (TMDL, PBIP, or .pbix) onto Malloy - read their model, transpile its DAX, and prove the numbers match. Use when Power BI artifacts are present, when a user wants to migrate or switch off Power BI, and whenever a specific DAX measure has to become Malloy - CALCULATE, ALL/ALLSELECTED/ALLEXCEPT, RANKX and top-N, time intelligence, USERELATIONSHIP, calculation groups, what-if parameters, or an RLS role. Carries a worked, executed transpile per construct and states what each port costs. Works with or without a database connection.
4
- version: 0.1.51
4
+ version: 0.1.52
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.51
4
+ version: 0.1.52
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: Read before writing or debugging a Malloy query. Dates, aggregates vs dimensions, joins, filters, strings, window functions, chart annotations, and the compile errors a SQL habit produces.
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
  <!--
7
7
  Copyright (c) Credible Data Inc.
@@ -111,6 +111,28 @@ run: candidate -> { aggregate: hiring_manager.employee_count }
111
111
 
112
112
  Use the field paths defined in the model **verbatim**. If the model defines `hiring_manager.employee_count`, do not strip the `hiring_manager.` prefix; that prefix is the join namespace, not a separate source to navigate to.
113
113
 
114
+ ### Record and array columns
115
+
116
+ A dotted name is not always a join. A record column (a BigQuery RECORD, a DuckDB STRUCT) is read the same way: `group_by: delivery.city`. A field whose first part is in the source's join list is reached through a join. A field whose parent is a field of type `record` or `array` is part of the row.
117
+
118
+ - **A record member** (`delivery.city`) behaves like any other column: one value per row.
119
+ - **A member of an array of records** (`lines.sku`) and **the values of an array of plain values** (`tags.each`) have `relationship: "many"`: one value per element. An array of plain values is read with `.each` and nothing after it.
120
+
121
+ Counting an array has two forms, and they answer different questions:
122
+
123
+ | Write | Answers |
124
+ |---|---|
125
+ | `count(tags.each)`, `count(lines.sku)` | How many **distinct** values |
126
+ | `tags.count()`, `lines.count()` | How many **elements**, across all rows |
127
+
128
+ `tags.each.count()` and `lines.sku.count()` fail with `is not a source or join`, like any method `count` on a field.
129
+
130
+ Grouping by a `many` field gives one group per distinct element value, and a row lands in every group its array holds: orders tagged `['r','r','b']` and `['r']` give `r` a `count()` of 2, not 3. A measure in each group is still correct (`revenue` for the tag `red` counts each order once), but the groups add up to more than the total, because an order with two tags is in two groups. Also:
131
+
132
+ - Alias it: `group_by: tag is tags.each`. Unaliased, the column is named `each`.
133
+ - A row whose array is empty lands in a `null` group.
134
+ - Two arrays in one query pair every element of one with every element of the other.
135
+
114
136
  ## Dates and Time
115
137
 
116
138
  ### Comparisons need `@` literals
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: malloy-reports
3
3
  description: "Build or modify a Malloy Publisher report: a .malloy file with an ## artifact tag, written at the package root unless asked otherwise, laid out as a grid (a dashboard, scanned at a glance) or linear (a notebook or data story, read top to bottom), with filter controls rendered from givens and # drill click-through. Use when the user asks for a dashboard, a notebook, a report, a data story, a filterable view, or drill-through between views, and no code is wanted. Legacy .malloynb files are read, not authored."
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
 
7
7
  <!--
@@ -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.51
4
+ version: 0.1.52
5
5
  ---
6
6
  <!--
7
7
  Copyright (c) Credible Data Inc.
@@ -126,7 +126,7 @@ For every rule, the linked instruction-skill section is the canonical source for
126
126
  - **`SUM(x) OVER (PARTITION BY ... ORDER BY ... ROWS UNBOUNDED PRECEDING)`** → `calculate: c is sum_cumulative(x) { partition_by: ...; order_by: ... asc }`
127
127
  - **`SUM(x) OVER (... ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)`** (cumulative-excluding-current) → `sum_cumulative(x) - x` with the same `partition_by`/`order_by`
128
128
  - **Multi-CTE pipelines** → stacked query-based sources: `source: a is t -> {...}`; `source: b is a -> {...}`
129
- - **UNNEST / array access** → an array of records is read by its field path (`group_by: ys.yr`, `aggregate: tv is ys.v.sum()`); an array of plain values is read with `.each` and nothing after it (`group_by: v is arr.each`). `array_column.each.field` does not exist ([data types docs](https://docs.malloydata.dev/documentation/language/datatypes#array-access))
129
+ - **UNNEST / array access** → an array of records is read by its field path (`group_by: ys.yr`, `aggregate: tv is ys.v.sum()`); an array of plain values is read with `.each` and nothing after it (`group_by: v is arr.each`). `array_column.each.field` does not exist. A measure meant to count elements must be `arr.count()`, not `count(arr.each)`, which is the distinct count; flag the swap ([data types docs](https://docs.malloydata.dev/documentation/language/datatypes#array-access))
130
130
  - **PIVOT / `SUM(CASE WHEN cat = 'a' THEN x END)`** → filtered aggregates: `aggregate: a is x.sum() { where: cat = 'a' }`
131
131
  - **`WHERE date = (SELECT max(date) FROM …)`** (latest snapshot) → `join_cross` to a one-row aggregate source, then filter on the joined `max_date` field
132
132
  - **Multi-key joins (`ON a.x = b.x AND a.y = b.y`)** → `join_one: b on x = b.x and y = b.y`
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: malloy-run-agent
3
3
  description: Adopt an agent a Malloy package declares, by name. Use only when the user names a package agent, for example "run the analyst agent on storefront" or "work as the agent in this package". Fetches the agent's definition with get_agent, works from its instructions, installs its skills, and runs one of its tasks when asked.
4
- version: 0.1.51
4
+ version: 0.1.52
5
5
  ---
6
6
  <!--
7
7
  Copyright (c) Credible Data Inc.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: malloy-source-unreachable
3
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.51
4
+ version: 0.1.52
5
5
  ---
6
6
  <!-- Copyright (c) Credible Data Inc. SPDX-License-Identifier: MIT -->
7
7