malloy-modeling
Skill132 starsChanged 9 days agoMITView on GitHub
Build semantic models with Malloy for the Malloy Publisher. Read this skill whenever the user asks about modeling data or specifically mentions Malloy.
In this repository37 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- fix-scan-findingSkill
- publisher-releaseSkill
- eval-answerSkill
- eval-diagnoseSkill
- eval-importSkill
- eval-improveSkill
- eval-judgeSkill
- eval-loopSkill
- eval-reportSkill
- malloy-analysis-reportSkill
- malloy-analysisSkill
- malloy-chartsSkill
- malloy-classifySkill
- malloy-compliance-reviewSkill
- malloy-dashboardsSkill
- malloy-define-agentSkill
- malloy-defineSkill
- malloy-discoverSkill
- malloy-documentSkill
- malloy-getting-startedSkill
- malloy-gotchas-modelingSkill
- malloy-html-data-appsSkill
- malloy-lookml-reviewSkill
- malloy-materializationSkill
- malloy-model-as-you-goSkill
- malloy-modelSkill
- malloy-notebooksSkill
- malloy-phrase-detectionSkill
- malloy-powerbi-reviewSkill
- malloy-publishSkill
- malloy-queriesSkill
- malloy-reportsSkill
- malloy-reviewSkill
- malloy-run-agentSkill
- malloy-source-unreachableSkill
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 4 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 19
Never guess field names either way.
- Very specific: line 20
Search docs proactively: call search_malloy_docs BEFORE writing unfamiliar patterns (window functions, query-based sources, pipelines).
- Distinctive: line 22
Check diagnostics: after writing: fix the FIRST error first, errors cascade.
- Very specific: line 28
Alias joined fields in
group_by: if using them in order_by - Very specific: line 31
No fixed scale on measures: use # currency not # currency=usd0m
- Very specific: line 33
Boolean columns: use = true not = 'true' (no quotes!)
- Distinctive: line 38
If the IDE has a native plan mode, use it for the high-level approach: do data exploration during planning, then present a concrete plan …
- Distinctive: line 42
Keep it compact, with these sections:
- Very specific: line 109
Present choices as A/B/C: When asking the user to choose, use lettered options with one-line descriptions.
- Very specific: line 145
Never guess field names. Ground yourself with get_context to see the sources and fields a package defines.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
<!--
Copyright (c) Credible Data Inc.
SPDX-License-Identifier: MIT
-->STOP - READ BEFORE WRITING ANY MALLOY CODE
AI AGENTS: You MUST review this file before writing Malloy code. Cross-skill references below use logical
skill:names; load the referenced skill before acting. Before writing code, also read the gotcha skills:skill:malloy-gotchas-modeling,skill:malloy-queries, andskill:malloy-charts.
Pre-Flight Checklist
- Discover first: ground yourself before writing ANY code, with the tool that matches what you are modelling.
- Modelling data already in a package:
get_contextreturns that package's sources, views, and fields (with their docs). - Modelling a database with no package yet:
get_contexthas nothing to return, so usesearch_database_schemainstead. It walks the connection's schemas and tables, ranks them against a plain-English description, and gives you each table's columns plus thesource:line to start from. Take those names verbatim into step 5. Never guess field names either way.
- Modelling data already in a package:
- Search docs proactively: call
search_malloy_docsBEFORE writing unfamiliar patterns (window functions, query-based sources, pipelines). Don't guess. Malloy syntax is specific and SQL intuition is often wrong. - Search by topic name.
search_malloy_docstakes plain topics such as "window functions", "comparing timeframes", "cohort analysis", "percent of total", "histogram", "nesting", "rendering". - Check diagnostics after writing: fix the FIRST error first, errors cascade.
- Read the gotcha skills:
skill:malloy-gotchas-modeling,skill:malloy-queries, andskill:malloy-chartsprevent the most common mistakes.
Quick syntax reminders:
- Backtick reserved words:
`Date`,`Hour`,`Timestamp`,`Type`,`number`,`source` - Use
having:for aggregate filters: notwhere:on measures - Alias joined fields in
group_byif using them inorder_by count()counts rows;count(x)counts distinct values ofx:count(distinct x)is deprecated, writecount(x)- One tag per line:
# label="Revenue"and# currencyon separate lines - No fixed scale on measures: use
# currencynot# currency=usd0m - Cast strings for aggregates:
avg(score::number)notavg(score) - Boolean columns: use
= truenot= 'true'(no quotes!) - Read data files in place:
.csv,.parquet,.json,.ndjson, and.xlsxall work as-is throughduckdb.table('data/file.ext'). Never convert a file to another format first, and never read one with python or jq to "have a look" first: query it. For.xlsx, check the row count before trusting it: a workbook with a title row or a blank spacer reads short and reports no error. (Per-format quirks:skill:malloy-gotchas-modeling)
Planning and modeling-notes.md
If the IDE has a native plan mode, use it for the high-level approach: do data exploration during planning, then present a concrete plan for user approval before writing any files.
modeling-notes.md is an expected output of the workflow, not an optional extra. Start it at step 2 (Propose Scope) and grow it as you work: it persists alongside the model, and its value is as the thing the user argues with at step 3, before source files exist; written after the build it can only document decisions already baked in. Record findings and problems as they are found during discovery (skill:malloy-discover), and every unconfirmed decision as an open item. Only when there is no writable workspace do the notes live in the conversation instead.
Keep it compact, with these sections:
# Modeling notes - <package>
## Scope what was confirmed, what the model is FOR, skip list with reasons
## Grain and keys proven by query, not by column name
## Coverage coverage cliffs; columns excluded for nullity
## Decisions each with its evidence
## Open decisions ASSUMPTIONS, NOT CONFIRMED: every threshold or definition the user
has not settled, one entry each, mirrored by a hedge in its #(doc)
## Validation reconciliation checks performed, and their results8-Step Modeling Workflow
The agent orchestrates all steps. Steps marked (user) pause for input. Each step has a dedicated skill with full instructions. Read each step's skill before starting that step, including the decision skills for steps 1–4 (skill:malloy-discover, skill:malloy-define). They govern what the model says; skipping them to reach the build skills is how unreviewed business logic ships.
A field is not complete until it has its definition, #(doc) tag, and rendering tags, and any threshold or business convention in it is user-confirmed, distribution-derived, or explicitly flagged in its #(doc) (see skill:malloy-document § Mark conventions as conventions). Documentation is part of defining a field, not a separate activity. Read skill:malloy-document for full documentation standards (doc string writing, tag ordering).
DISCOVER → SCOPE → SOURCES → DEFINITIONS → BUILD BASE → BUILD JOINED → REVIEW → CURATE
(silent) (user) (user) (user) (agent) (agent) (user) (user)| Step | Skill | What Happens |
| 1. Discover | skill:malloy-discover | Read the model and data; scan sources, fields, distributions; detect prior art. With no package yet, start from search_database_schema to find the tables in the connection |
| 2. Propose Scope | skill:malloy-define (Propose the analytical scope) | Present findings, user selects focus |
| 3. Propose Sources | skill:malloy-define | Propose source plan, user confirms architecture |
| 4. Propose Definitions | skill:malloy-define | Propose fields per base source, user confirms logic |
| 5. Build Base Sources | skill:malloy-model | Write fully documented base source files (one per table), check diagnostics. Read skill:malloy-document for doc standards. |
| 6. Build Joined Sources | skill:malloy-model | Write fully documented joined source files, validate. Read skill:malloy-document for doc standards. |
| 7. Review | (none) | Present the review checklist below; user confirms or corrects |
| 8. Curate | skill:malloy-model | Propose the published surface (an index.malloy with export { ... }) and access controls (gates, private: fields; internal: is curation only); always propose, the user decides whether to apply |
The pauses are the point
These are governed semantic models: the business decisions in them must be confirmed by a human subject-matter expert, and the (user) steps exist to collect that confirmation. They are real stops, not progress reports. A model can be complete, compiling, and fully documented and still be wrong everywhere it guessed; a capable agent can build the whole thing without pausing once, which is exactly the failure mode this workflow exists to prevent.
When a decision goes unanswered (the user explicitly declines to decide, or nobody is there to ask), do not silently proceed as if it were settled. Take your best-supported position, label it an assumption in the field's own #(doc) (see skill:malloy-document § Mark conventions as conventions), record it under "Open decisions" in modeling-notes.md, and raise it again at Review. An unlabeled assumption is indistinguishable from a confirmed fact, and misleads everyone downstream.
Step 7 Review is a checklist, not a summary
Present these to the user, with answers:
- Which definitions did the user actually confirm? List them; everything else is an assumption.
- Which thresholds and bucket boundaries did you choose? For each: the evidence (distribution query, metadata, prior art) and the
#(doc)hedge that marks it. - Which questions were left unanswered? Each must already carry a labeled assumption and an "Open decisions" entry.
- Does the headline metric have more than one defensible definition? If yes, that is a blocking question: put the candidate definitions to the user with their counts side by side, not in a footnote.
The user confirming this checklist is what makes the model governed. A summary of what you built is not a checkpoint.
Publishing is out of scope for open-source v1. Self-hosters move a finished model into a served package via git and the host's publish path; see skill:malloy-publish for the local-to-served handoff.
Two paths to a model: both produce the same fully documented result:
- Schema-first: "Model my data" → 8-step workflow above using the relevant skills
- Analysis-first: a data question arrives before any model exists →
skill:malloy-model-as-you-go. It answers the question withskill: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. - Open-ended exploration with no intent to keep anything:
skill:malloy-analysis(its "no specific question" branch). If it turns into something worth keeping, formalize viaskill:malloy-model(reference/analysis-to-model.md).
Agent Behavior
Research before asking. Present proposals with evidence. Never ask open-ended questions: propose with data and let the user confirm.
Use business language. Say "I simplified the column name" not "reserved word replaced." Don't expose Malloy internals unless the user asks.
Describe what you're doing, not which step you're on. The user doesn't have the skill files open. Say "I'll propose which tables to include and how they relate" not "Steps 3 and 4." Say "Now I'll write the source files" not "Moving to Step 5." Explain the purpose of each phase in plain language before doing it.
Present choices as A/B/C. When asking the user to choose, use lettered options with one-line descriptions. Mark your recommendation.
Complete all workflow steps. Once modeling begins, complete through Review and propose Curate. A field without documentation is not finished. If you lose track, re-read the model and your notes. Every source you write gets a source-level #(doc) description and a stated grain (primary_key: or "one row per ..."), including intermediate steps such as a de-duplication or clean-up source. Or fold the clean-up into the one source it feeds, so there is nothing undescribed left in the package. Build a report (a dashboard or a notebook) only when the user asks for one.
Route by Intent
| User says... | Route to |
| "Model my data", "create a model" | 8-step workflow (skill:malloy-discover) |
| "Model from LookML" | 8-step with prior art via skill:malloy-lookml-review |
| "Explore this data", "what's interesting?", "show me the top X" | skill:malloy-analysis (no specific question) |
| "Build a dashboard", "build a notebook", "create views" on existing model | skill:malloy-reports for a saved report (layout=grid for a dashboard, layout=linear for a notebook), written at the package root; skill:malloy-charts for views in the model |
| "Build a model but not sure what metrics" | skill:malloy-model-as-you-go: answer their first real question, codify what it assumed, repeat |
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.
Additional Support Skills
These supplemental skills may also be loaded as needed:
skill:malloy-getting-started: routing guide to the other Malloy skillsskill:malloy-gotchas-modeling: also the place to fix compile errors and read diagnostics
Publisher MCP Tools
Modeling needs these tools, or their REST equivalents when you run unattended; skill:malloy-getting-started section 0 covers both cases. No server yet? skill:malloy-getting-started covers setup, including the one-command scaffolder (npm create @malloy-publisher/malloy-package@latest <name>) and why local authoring needs --watch-env <env>: start the server without it and your saved edits are never read.
| Tool | Purpose |
get_context | Ground yourself in a package: its sources, views, and fields |
execute_query | Run ad-hoc queries for validation |
compile_model | Compile-check a change and get diagnostics back without running a query |
reload_package | Recompile a package from disk so a saved edit becomes queryable by name |
search_malloy_docs | Search Malloy docs (call BEFORE unfamiliar patterns) |
search_database_schema | Find the tables in a database connection by plain-English description, when modelling data that is not in a package yet. Returns each table's columns and the source: line to start from. Names and types only: no row value is returned |
Never guess field names. Ground yourself with get_context to see the sources and fields a package defines.
The edit-and-run loop
Publisher compiles each configured package at boot and serves that cached model, so a source or view you add afterwards is not queryable by name until you reload the package. The loop is:
- Validate the change with
compile_model, picking the scope that matches what you are doing:- Adding a new definition or query: the default (
scope: "append") compiles your text in the model's namespace. Note its diagnostic positions land in the model-plus-your-text concatenation. Append checks your text against what the model already publishes, so it refuses text that declares its own data root --import,connection.table(...)andconnection.sql(...)are rejected. Asource:line handed to you bysearch_database_schemais exactly that shape, so validate it atfileorpackagescope instead. - Editing an existing definition:
scope: "file", with the whole edited file assource. It compiles your text AS the file (append would collide with "Cannot redefine"), and diagnostics land at the true line numbers of your text. - Before saving a change other files import:
scope: "package"with the edited file assourceruns reload's worker compiler over every.malloyand.malloynbfile against your edit, so a rename that breaks an importer surfaces now instead of at reload. Each diagnostic carriesmodel, the file it points at; files hidden from discovery can appear. IfmodelPathdoes not exactly match an existing file, a warning says the source was treated as new.
- Adding a new definition or query: the default (
- Save it to the package's model file.
- Reload with
reload_package. - Run the new view with
execute_query.
A reload that fails to compile is safe: your files are left alone and the previously compiled model keeps serving, with the compile errors returned to you. Compile first anyway for faster feedback, and a scope: "package" dry-run with no source uses reload's compiler and file selection (imports across files, every .malloy and .malloynb file as saved) without touching the served model. Keep the source of truth outside publisher_data/, which is not version-controlled and is wiped by a --init restart. If these tools are missing, the Publisher you are connected to predates them; fall back to validating with a throwaway execute_query. An older Publisher that has compile_model but rejects scope supports only the append behavior.
SQL-to-Malloy Quick Reference
| SQL | Malloy |
COUNT(*) | count() |
COUNT(DISTINCT x) | count(x) |
NOW() | now |
CASE WHEN...END | pick...when...else |
col IN ('a','b') | col ? 'a' | 'b' |
COALESCE(a,b) | a ?? b |
CAST(x AS type) | x::type |
DATEDIFF(day, a, b) | days(a to b) |
CONCAT(a, b) or a || b | concat(a, b) |
TIMESTAMP_DIFF(a, b, SECOND) | seconds(b to a) |
Critical Rules
- All keywords require colons:
source:,dimension:,measure:,view: - Use
isnotas:dimension: name is expression - Arrow operator required:
run: source -> { operations } - Specify join type:
join_one:,join_many:,join_cross: - Safe division:
revenue / nullif(count, 0) - Group definitions under one keyword:
measure:then indent fields beneath
Common Anti-Patterns
WRONG: source flights is ... RIGHT: source: flights is ...
WRONG: dimension: x as y RIGHT: dimension: y is x
WRONG: count(*) RIGHT: count()
WRONG: count(distinct x) RIGHT: count(x)
WRONG: revenue / order_count RIGHT: revenue / nullif(order_count, 0)
WRONG: run: src { ... } RIGHT: run: src -> { ... }Reserved Words: Scan Schema First
Malloy has many reserved words. When in doubt, backtick it. Most likely to appear as column names:
date, time, day, month, year, quarter, week, hour, minute, second,
number, string, boolean, type, table, source, index, count, sum, avg, min, max,
true, false, null, is, on, with, all, from, by, in, to, for, select, order_by,
top, bottom, desc, asc, row, range, current, window, ranknumber: only the bare word needs backticking;account_numberis finesource: reserved; use a different alias liketraffic_sourcestring,boolean,true,false: backtick any column with these exact names
Gotcha Skills: Read Before Writing Code
The following skills contain detailed WRONG/RIGHT patterns that prevent the most common Malloy errors. Read them before writing code:
skill:malloy-gotchas-modeling: Reserved words, NULL checks, date functions, type casts, rename pitfalls, query-based source gotchas,conn.sql()anti-patternskill:malloy-queries: Syntax and the common compile errors: chart constraints, aggregate filters, joined field aliasing, method syntax, time truncation vs extractionskill:malloy-charts: Chart selection, tag syntax, scale rules, sparkline setup, big_value patterns
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

