| name | malloy-review |
| 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. |
Malloy Code Review
Single-pass code reviewer for .malloy files. The deliverable is one Markdown file in the canonical shape (see reference/output-template.md). Findings cite the project's standards file (e.g., CLAUDE.md) where it applies; otherwise they cite rubric IDs (reference/rubric-*.md).
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.
Before you start
Read these in order:
- Project standards: whatever conventions exist in your host environment (
CLAUDE.md, AGENTS.md, etc.). Treat as the higher-priority source of truth; rubric rules defer to it where they overlap.
reference/severity-taxonomy.md: the vocabulary every finding uses.
reference/rubric-*.md: the seven dimension rubrics. Don't re-read for every review; load them on demand based on what's in scope.
reference/output-template.md: the shape of the review file you produce.
.malloy-review.local.md in the scope folder, if present, project-specific severity overrides or extra rules.
Make sure the Malloy MCP tools are configured before running: this skill uses execute_query for data checks and search_malloy_docs for verifying Malloy capabilities. Both are optional; the review degrades gracefully if either is unavailable.
Inputs
/malloy-review [<path>] [--pr <n>] [--out <file>] [--comment]
| Argument | Effect |
|---|
| (no arg) | Auto-detect scope per reference/scope-resolution.md |
<path> | Review that file or directory |
--pr <n> | Review the .malloy files changed in GitHub PR <n> (see PR mode below) |
--out <file> | Write review to this path. Default is ./malloy-review-<YYYYMMDD-HHMMSS>.md |
--comment | PR mode only: post the review as a PR comment via gh pr comment |
If scope is ambiguous (multiple packages, wrong file type, empty result), stop and ask: don't guess. See reference/scope-resolution.md for the rules.
Workflow
resolve scope → read files → verify PKs → apply rubrics → write output
1. Resolve scope
Per reference/scope-resolution.md. Echo the resolved scope to the user before doing anything expensive. Multi-package repos → present packages as A/B/C and let the user pick. Never auto-fan-out across packages: different packages may target different database connections.
2. Read files
For each in-scope .malloy file, read the full content. As you read, track each source's primary_key: and its join_one/join_many/join_cross targets, you'll use this for the PK uniqueness check (C-12) and join-style consistency (Y-03).
If mcp__ide__getDiagnostics is available, call it on the in-scope files and promote each diagnostic to a finding: errors → blocker (confidence 95), warnings → major (confidence 85), info/hint → minor (confidence 75), all with source: "diagnostic". Skip rubric rules these already cover. If unavailable, skip, the LLM rubric pass still runs.
3. PK data verification (if execute_query is available)
For every source with a declared primary_key:, run a uniqueness check and store the result on source_index.<src>.pk_verified:
run: <source> -> {
aggregate:
rows is count()
distinct_pk is count(<pk_col>)
}
| Result | pk_verified |
|---|
rows == distinct_pk | true |
rows != distinct_pk | false |
execute_query is unavailable for the scope | "skipped" |
| The query fails (source unreachable, connection error, etc.) | "error" |
This step only collects evidence, the emit decision and fix template live in reference/rubric-correctness.md § C-12. When any source is "skipped" or "error", note the coverage gap in the output's Scope section.
4. Apply rubrics
Score the in-scope files against each applicable rubric. You don't need to read all seven: pick by content:
| Rubric | Read when |
|---|
rubric-correctness.md | Always |
rubric-documentation.md | Always (every source/measure/dimension should be documented) |
rubric-style.md | Always |
rubric-structure.md | Always |
rubric-queries.md | Any in-scope file has view: or run: |
rubric-rendering.md | Any in-scope file has a # rendering tag |
rubric-governance.md | Any in-scope file has ##! experimental.access_modifiers or include {} blocks |
Findings use the canonical shape from reference/severity-taxonomy.md:
{
"id": "C1",
"severity": "critical",
"category": "correctness-join",
"file": "packages/x/customers.malloy",
"line_range": [12, 12],
"rule": "C-12 declared primary_key is not unique in the data",
"current": "primary_key: customer_id (customer_id has duplicates per execute_query check)",
"expected": "customer_id is unique per row, or the source carries a where: that scopes to a uniquely-keyed subset",
"suggested_fix": "...",
"confidence": 95,
"source": "rule"
}
Drop findings with confidence < 80. The output should feel curated, not exhaustive.
5. Look for cross-cutting themes
After per-file findings, scan for systemic patterns. This is the highest-value output. Examples that emerged from real reviews:
- N base sources all missing
include {} curation
- M files use raw
conn.sql() where Malloy-native patterns exist
- Canonical naming violations across N files (
total_revenue vs net_revenue, etc.)
- "Junk-drawer" files with multiple unrelated sources
Promote these to a Cross-Cutting Themes section in the output. They are usually more actionable than per-line findings, collapse 3+ findings of the same rule into one theme with the file list, and emit individual findings only for the top 2–3 worst offenders.
6. Assemble output
Apply reference/output-template.md. Section order is fixed (skip if empty):
- Header (scope, file count, LOC, timestamp)
- Scope (paths reviewed)
- Executive Summary (recommendation + top 1–3 risks)
- Coverage & Risk Map (file table; skip if scope is one file)
- Cross-Cutting Themes
- Top Issues (blockers + criticals)
- Detailed Findings (collapsed
<details> per file)
- Questions for the Author (≤5 bullets)
- Positive Notes (only if real)
- Suggested Follow-ups (aggregated minor/nit findings)
- Suggested Split (only when scope is >2000 LOC or >30 files)
7. Write the file
Default ./malloy-review-<YYYYMMDD-HHMMSS>.md. Tell the user the path and the top 1–3 issues inline. End with an offer to start fixing, most reviews are the start of iterative work, not a static report.
Scaling notes
| Scope | Behavior |
|---|
| ≤5 files / ≤500 LOC | Trim Coverage Map and Cross-Cutting sections, likely nothing to surface. Skip the Suggested Split section. |
| 6–20 files / 500–2000 LOC | The canonical workflow above. Include all sections that have content. |
| >20 files / >2000 LOC | Add a mandatory Suggested Split section. Risk-tier files into DEEP / SAMPLE / SKIM (DEEP = top ~25% by content signals: joins, access modifiers, source-level where:, public surface). Cap per-file finding count at ~12 per dimension; promote overflow into Cross-Cutting Themes. Blocker, critical, and diagnostic findings never count against the cap. |
Mode notes
Single-file mode (user passes one .malloy file): trim the template hard. Drop the Coverage Map, Cross-Cutting Themes, and Suggested Split sections. Lean conversational; write the file AND print the Summary + Findings inline so the user doesn't need to open the file for a small review. End with a fix offer ("Want me to fix B1?").
Audit mode (user passes a directory or invokes inside a publisher.json package): every file is in play. Coverage Map matters more, the user needs to know what was deep-reviewed vs. skimmed. For unfamiliar packages, consider running just the source-level summary first, presenting it, and asking whether to proceed with full review.
PR mode (--pr <n>):
gh pr view <n> --json state,isDraft,title,baseRefName,headRefName,url,author and gh pr diff <n> --name-only. Stop if the PR is closed or has no .malloy changes.
- Filter changed files to
.malloy and intersect with any path argument, that's the scope.
- Fetch full file contents on the PR's head (via
gh pr checkout <n> if the working tree is clean, otherwise via gh api repos/<owner>/<repo>/contents/<path>?ref=<headRef>). Don't clobber working state without asking.
- Run the workflow above. PR header:
# Malloy Model Review, PR #<n>: <title> with branch/package/LOC/url metadata.
- With
--comment, post via gh pr comment <n> -F <file> (cap at GitHub's 65,536-char limit; if larger, post the exec summary + top issues and reference the local file). Lead the comment body with <!-- malloy-review: <timestamp> --> so re-runs can detect prior reviews. Never post without --comment.
What this skill does NOT do
- Does not modify any
.malloy files. Output is the Markdown review only.
- Does not post PR comments without
--comment.
- Does not walk above the resolved scope. Even if a finding would benefit from cross-scope context, scope is fixed once resolved.
- Does not guess at multi-package disambiguation. Always asks.
- Does not flag modeling features as hazards. Source-level
where: clauses and deliberate private: choices are part of how Malloy models work. If something looks unusual, surface it as a "Question for the Author," not a finding.