@malloy-publisher/skills 0.1.0
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/LICENSE +21 -0
- package/README.md +68 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +47 -0
- package/package.json +54 -0
- package/skills/malloy/SKILL.md +45 -0
- package/skills/malloy-analysis/SKILL.md +76 -0
- package/skills/malloy-analysis-pitfalls/SKILL.md +68 -0
- package/skills/malloy-analysis-report/SKILL.md +125 -0
- package/skills/malloy-analyze/SKILL.md +262 -0
- package/skills/malloy-charts/SKILL.md +417 -0
- package/skills/malloy-debug/SKILL.md +121 -0
- package/skills/malloy-define/SKILL.md +194 -0
- package/skills/malloy-discover/SKILL.md +182 -0
- package/skills/malloy-document/SKILL.md +165 -0
- package/skills/malloy-getting-started/SKILL.md +57 -0
- package/skills/malloy-gotchas-modeling/SKILL.md +277 -0
- package/skills/malloy-gotchas-queries/SKILL.md +132 -0
- package/skills/malloy-gotchas-rendering/SKILL.md +124 -0
- package/skills/malloy-html-data-app-embedding/SKILL.md +39 -0
- package/skills/malloy-html-data-app-runtime/SKILL.md +158 -0
- package/skills/malloy-html-data-apps/SKILL.md +165 -0
- package/skills/malloy-html-data-apps/reference/lazy-load.md +84 -0
- package/skills/malloy-html-data-apps/reference/verification-harness.md +121 -0
- package/skills/malloy-lookml-review/SKILL.md +79 -0
- package/skills/malloy-lookml-review/reference/_concepts.md +42 -0
- package/skills/malloy-lookml-review/reference/build-derived-tables.md +110 -0
- package/skills/malloy-lookml-review/reference/build-unnest.md +47 -0
- package/skills/malloy-lookml-review/reference/curate-visibility.md +66 -0
- package/skills/malloy-lookml-review/reference/discover.md +102 -0
- package/skills/malloy-lookml-review/reference/document.md +28 -0
- package/skills/malloy-lookml-review/reference/propose-fields.md +75 -0
- package/skills/malloy-lookml-review/reference/review-coverage.md +102 -0
- package/skills/malloy-materialization-tuning/SKILL.md +103 -0
- package/skills/malloy-model/SKILL.md +320 -0
- package/skills/malloy-model/reference/access-modifiers.md +21 -0
- package/skills/malloy-model/reference/analysis-to-model.md +82 -0
- package/skills/malloy-model/reference/bridge-tables.md +51 -0
- package/skills/malloy-model/reference/normalized-schemas.md +66 -0
- package/skills/malloy-model/reference/query-sources.md +57 -0
- package/skills/malloy-modeling/SKILL.md +174 -0
- package/skills/malloy-notebook-chat/SKILL.md +19 -0
- package/skills/malloy-notebooks/SKILL.md +225 -0
- package/skills/malloy-patterns/SKILL.md +117 -0
- package/skills/malloy-phrase-detection/SKILL.md +75 -0
- package/skills/malloy-publish/SKILL.md +111 -0
- package/skills/malloy-queries/SKILL.md +270 -0
- package/skills/malloy-review/SKILL.md +160 -0
- package/skills/malloy-review/reference/output-template.md +164 -0
- package/skills/malloy-review/reference/rubric-correctness.md +161 -0
- package/skills/malloy-review/reference/rubric-documentation.md +69 -0
- package/skills/malloy-review/reference/rubric-governance.md +37 -0
- package/skills/malloy-review/reference/rubric-queries.md +78 -0
- package/skills/malloy-review/reference/rubric-rendering.md +127 -0
- package/skills/malloy-review/reference/rubric-structure.md +68 -0
- package/skills/malloy-review/reference/rubric-style.md +59 -0
- package/skills/malloy-review/reference/scope-resolution.md +100 -0
- package/skills/malloy-review/reference/severity-taxonomy.md +139 -0
- package/skills/malloy-scope/SKILL.md +100 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Rubric: Rendering
|
|
2
|
+
|
|
3
|
+
**Dimension:** `rendering`
|
|
4
|
+
**Rules:** 9
|
|
5
|
+
|
|
6
|
+
Applies to any `.malloy` file with `view:` definitions or `#`-tagged fields. If the file has no rendering tags, skip this dimension.
|
|
7
|
+
|
|
8
|
+
For every rule, the linked instruction-skill section is the canonical source for rationale, valid values, and WRONG/RIGHT examples.
|
|
9
|
+
|
|
10
|
+
**A note on silent failure.** Malloy **silently ignores unknown rendering tags**, there's no compile error, no warning at runtime, no rendered chart, just a fallback to the default table view. R-09 below catches this. Run `mcp__ide__getDiagnostics` after editing chart tags; some malformed nested-tag syntax (e.g., `viz.stack.y` from `# bar_chart.stack { y=[...] }`) does surface as a renderer lint warning, which is strong evidence when present.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## R-01: Don't bake a fixed display scale into a measure definition
|
|
15
|
+
|
|
16
|
+
- **Severity:** minor (non-blocking) · **Category:** rendering · machine-checkable
|
|
17
|
+
- **Why this is a problem.** A scale like `# currency=usd1m` (display in millions) is correct for one specific value range, but a measure can be reused across many views. As soon as a view filters down, one customer, one month, one product, one region, the rendered values may fall well below the baked-in scale and round visibly: `$500` displays as `$0.0M`, `$1,200,000` collapses to `$1M` and loses precision. The scale belongs on the view (after you've seen the actual range), not on the measure definition where it can't anticipate every downstream filter.
|
|
18
|
+
- **Detection:** any explicit value on a `# currency`, `# number`, or `# percent` tag immediately above a `measure:` or `dimension:` declaration, e.g. `# currency=usd1m`, `# number=0,000`, `# percent=0.00`. The bare forms (`# currency`, `# number`, `# percent`) are correct and must not be flagged.
|
|
19
|
+
- **Fix:** drop the `=<scale>` from the measure or dimension. Add it at the view level for the specific views where the value range is known to fit.
|
|
20
|
+
- **See:** `skill:malloy-gotchas-rendering` § No Fixed Scale on Measures
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## R-02: `# big_value` measures should have `# label`
|
|
25
|
+
|
|
26
|
+
- **Severity:** minor (non-blocking) · **Category:** rendering · machine-checkable
|
|
27
|
+
- **Detection:** AST, a view with `# big_value` tag whose `aggregate:` declarations lack a `# label="..."` tag.
|
|
28
|
+
- **Fix:** add `# label="..."` above each aggregate.
|
|
29
|
+
- **Why non-blocking:** verified against Malloy 0.0.370, the view compiles fine and the card still renders without `# label`. Malloy falls back to the raw field name on the card (`total_revenue` instead of `Total Revenue`), which is a clarity hit but not a render failure. Treat it as a polish recommendation, not a merge gate.
|
|
30
|
+
- **See:** `skill:malloy-gotchas-rendering` § `# big_value` Needs `# label` on Each Measure
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## R-03: Sparklines on `# big_value` need both the `sparkline=` reference and a matching `# hidden` nested view
|
|
35
|
+
|
|
36
|
+
- **Severity:** major (blocking) · **Category:** rendering · machine-checkable
|
|
37
|
+
- **Detection:** AST, `sparkline=` is a `# big_value` property; sparklines aren't a generic chart feature, and `sparkline=` on any other view tag is silently ignored by the renderer. For any view tagged `# big_value` with `sparkline=<name>`, verify the view contains a `nest:` whose alias matches `<name>` and whose tag block carries both a chart tag (e.g. `# line_chart`, `# bar_chart`) and `# hidden`.
|
|
38
|
+
- **Fix:** put `# hidden` on the nested chart view, and make sure the nested view's name matches the `sparkline=<name>` reference on the parent.
|
|
39
|
+
- **See:** `skill:malloy-gotchas-rendering` § Sparkline Setup · `skill:malloy-charts` § Sparklines in KPI Cards
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## R-04: `# big_value` comparison deltas need `comparison_label` to pair with `comparison_field`
|
|
44
|
+
|
|
45
|
+
- **Severity:** minor (non-blocking) · **Category:** rendering · machine-checkable
|
|
46
|
+
- **Detection:** regex, `comparison_field` and `comparison_label` are `# big_value` properties (along with `down_is_good` and the sparkline pair from R-03); they aren't a generic chart feature. For any view tagged `# big_value` whose tag block contains a `comparison_field=` property, verify a `comparison_label=` is also present in the same tag block (multi-line braced form counts).
|
|
47
|
+
- **Fix:** add `comparison_label="..."` with a human-readable description (e.g., `"vs Last Month"`).
|
|
48
|
+
- **See:** `skill:malloy-gotchas-rendering` § Comparison Deltas · `skill:malloy-charts` § KPIs with Comparison Deltas
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## R-05: Currency codes and scale values must be valid
|
|
53
|
+
|
|
54
|
+
- **Severity:** major (non-blocking) · **Category:** rendering · machine-checkable
|
|
55
|
+
- **Detection:** regex the full `# currency=<value>` or `# number=<value>` string and validate the shape. Valid currencies: `usd`, `eur`, `gbp`, `jpy`, `cad`, `aud`, `chf`, `cny`, `inr`. Valid scale suffixes: `K`, `M`, `B`, `T`, `Q`, or `auto`. Decimals: integer 0–6 before the suffix.
|
|
56
|
+
- **Fix:** swap to a valid code/scale
|
|
57
|
+
- **See:** `skill:malloy-charts` § Field Formatting Tags
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## R-06: `# number=id` for integer columns that aren't quantities (years, IDs, zip codes, phone numbers)
|
|
62
|
+
|
|
63
|
+
- **Severity:** major (non-blocking) · **Category:** rendering · machine-checkable (data-driven, falls back to LLM-judgment)
|
|
64
|
+
- **Why this matters.** Malloy's default integer formatting adds thousand-separator commas. A year displays as `2,018` instead of `2018`, a zip code as `94,107` instead of `94107`, an account number as `1,234,567` instead of `1234567`. The number is *visibly wrong* on every chart and table until `# number=id` strips the formatting. Especially common with `year(ts)` extraction (`skill:malloy-gotchas-queries` § Time Truncation vs Extraction) and with any FK/PK column that surfaces on a chart axis or in a result table.
|
|
65
|
+
- **Detection, preferred (data-driven).** For every integer dimension in scope, run `execute_query`:
|
|
66
|
+
|
|
67
|
+
```malloy
|
|
68
|
+
run: <source> -> {
|
|
69
|
+
aggregate:
|
|
70
|
+
rows is count()
|
|
71
|
+
distinct is count(<col>)
|
|
72
|
+
min_v is min(<col>)
|
|
73
|
+
max_v is max(<col>)
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Decide per column:
|
|
78
|
+
- **Distinct-count ≈ row count** → per-row identifier (account/order/user/phone numbers). Should have `# number=id`.
|
|
79
|
+
- **Year-shaped** (small distinct set, values in `1900–2100`) or `year(ts)` extraction → identifier-flavored, should have `# number=id`. Same for zip-shaped (small set of length-5 numeric), phone-shaped (length-10/11 numeric), categorical-numeric codes.
|
|
80
|
+
- **Quantity-flavored** (values aggregate naturally, name suggests `_count` / `_total` / `_amount` / `_price` / `_quantity` / `_revenue`) → leave default formatting.
|
|
81
|
+
- **Detection, fallback (no `execute_query`).** Name + type heuristics, noted in the finding so the reviewer can confirm:
|
|
82
|
+
- Should-have-`# number=id` signals: name ends in `_id`, `_year`, `_zip` / `_zipcode`, `_phone`; column is a `year(...)` extraction; integer type with no arithmetic in any `aggregate:` referencing it.
|
|
83
|
+
- Should-NOT-have-`# number=id` signals: name ends in `_count` / `_total` / `_amount` / `_price` / `_quantity` / `_revenue` / `_cost`.
|
|
84
|
+
- **Fix:** add `# number=id` above the dimension or measure.
|
|
85
|
+
- **See:** `skill:malloy-charts` § Field Formatting Tags · `skill:malloy-gotchas-queries` § Time Truncation vs Extraction (year-as-int discussion)
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## R-08: Don't use `# hidden` as a model-visibility mechanism
|
|
90
|
+
|
|
91
|
+
- **Severity:** nit (non-blocking) · **Category:** rendering · LLM-judgment
|
|
92
|
+
- **What `# hidden` is.** A Malloy *renderer* tag, cosmetic output suppression. Legitimately used inside `# dashboard` and `# big_value` views: hiding the `nest:` that only exists to feed a sparkline, hiding the `prior_month` aggregate that backs a comparison delta, hiding a tile from a dashboard layout. It does NOT remove a field from the source's API surface, the field is still queryable, still appears in source metadata, still a valid `group_by` / `aggregate` reference. See `skill:malloy-charts` § Utility Tags.
|
|
93
|
+
- **The mistake this rule catches.** `# hidden` placed on a `dimension:` or `measure:` declaration inside a source's `extend {}` block (not inside a view), used as if it were an access modifier. It isn't. If the goal is "remove from the source's public API," the right tools are `internal:` (or `private:` for sensitive data) in an `include {}` block, see `malloy-model/reference/access-modifiers.md`. The malloy-lookml-review skill calls this out explicitly: "LookML `hidden: yes` ≈ Malloy `# hidden` (cosmetic). LookML `fields` exclusion ≈ Malloy `internal:` (structural). Do NOT map `hidden: yes` directly to `internal:`."
|
|
94
|
+
- **Detection (LLM-judgment).** Flag `# hidden` on a `dimension:` or `measure:` inside a source's `extend {}` block. `# hidden` *inside a view*, on a `nest:` for sparklines, on a comparison aggregate inside `# big_value`, on a tile in `# dashboard`, is correct and must not be flagged.
|
|
95
|
+
- **Fix:** classify intent first.
|
|
96
|
+
- **"Remove from the API surface"** → move the column to `include { internal: <col> }` (or `private:` for sensitive) and drop the `# hidden`.
|
|
97
|
+
- **"Suppress in a specific view's rendered output"** → drop `# hidden` from the `extend {}` declaration and re-add it inside the specific view that needs the suppression.
|
|
98
|
+
- **See:** `skill:malloy-charts` § Utility Tags (canonical `# hidden` definition) · `malloy-model/reference/access-modifiers.md` (for the `internal:` / `private:` alternative)
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## R-09: Chart-rendering tags must be on the renderer's supported list
|
|
103
|
+
|
|
104
|
+
- **Severity:** major (blocking) · **Category:** rendering · machine-checkable
|
|
105
|
+
- **Why it matters.** The Malloy *core compiler* accepts any `# <tag>` annotation, verified against Malloy 0.0.370, where `# stacked_area_chart`, `# pie_chart`, and `# heatmap` all compile cleanly with no errors or warnings. Whether the renderer recognises the tag or silently drops it (rendering a flat table where the author expected a chart) is decided downstream in `@malloydata/malloy-render`. A real case: a view tagged `# stacked_area_chart` looked right in the code and rendered as a single-bar chart at runtime.
|
|
106
|
+
- **Detection, preferred (consume IDE diagnostics).** Recent versions of the Malloy renderer surface unknown render tags as `"Unknown render tag"` warnings with source-located info. When the host's renderer is on a recent-enough version *and* the IDE bridges renderer warnings into `mcp__ide__getDiagnostics`, the diagnostic pre-pass (SKILL.md step 2) is the right detection path, the rubric should NOT re-emit. Older renderers and hosts that don't bridge renderer warnings into diagnostics still need the explicit check below.
|
|
107
|
+
- **Detection, fallback (no diagnostics).** Flag every `# <tag>` annotation preceding a `view:` declaration where the tag isn't on the renderer's current allowlist. **Do not trust the rubric's hard-coded list as canonical**, call `search_malloy_docs` for "chart types" before flagging, since the supported set evolves (the renderer-validation contract is one such evolution). As a baseline, the long-stable set is `bar_chart | line_chart | scatter_chart | shape_map | segment_map | big_value | dashboard | list | list_detail`; treat anything outside that set as a candidate finding only after `search_malloy_docs` confirms it isn't a newly-added supported type.
|
|
108
|
+
- **Fix template (still useful regardless of detection path):**
|
|
109
|
+
- For "stacked area / stacked column" intent → `# bar_chart { stack y=['col1', 'col2', 'col3'] }` (see R-10 for the exact syntax).
|
|
110
|
+
- For "pie / donut" intent → not supported; redesign the view as a bar chart or use a custom renderer.
|
|
111
|
+
- For "heatmap" intent → not supported natively; use `shape_map` / `segment_map` if geographic, otherwise present as a 2D-pivot table.
|
|
112
|
+
- When `search_malloy_docs` reveals a newly-supported type that fits the intent, prefer that over the workaround.
|
|
113
|
+
- **See:** `skill:malloy-charts` § Chart Types
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## R-10: Multi-series chart syntax: flat properties + quoted strings
|
|
118
|
+
|
|
119
|
+
- **Severity:** major (blocking) · **Category:** rendering · machine-checkable
|
|
120
|
+
- **Detection:** when a chart tag attempts multi-series rendering via a `y=[...]` array, two specific bugs are common and BOTH produce silent partial rendering (only the first series shows up):
|
|
121
|
+
1. **Nested-tag form**, `# bar_chart.stack { y=[...] }` causes the renderer to parse `.stack` as a nested tag path and reject `y` as an unknown sub-property. The IDE lint reports `Unknown render tag 'viz.stack.y' on field 'root'`, that warning is strong evidence when present.
|
|
122
|
+
2. **Bare identifiers in the array**, `y=[col1, col2]` (no quotes) parses as something other than a string list and renders only the first measure. Quoted strings are required: `y=['col1', 'col2']`.
|
|
123
|
+
- **Fix template:**
|
|
124
|
+
- Replace `# bar_chart.stack { y=[...] }` → `# bar_chart { stack y=['col1', 'col2'] }` (stack as a sibling property).
|
|
125
|
+
- Replace `y=[col1, col2, ...]` → `y=['col1', 'col2', ...]` (quoted strings).
|
|
126
|
+
- **Why it matters:** the Malloy upstream docs' example uses the nested-tag form (`# bar_chart.stack { y=[...] }`), which is misleading, the renderer expects the flat form. Both forms are documented, but only one parses correctly in the actual renderer.
|
|
127
|
+
- **See:** `skill:malloy-charts` § Multi-Series Bar Charts · `skill:malloy-charts` § Stacked Bar Charts
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Rubric: Structure
|
|
2
|
+
|
|
3
|
+
**Dimension:** `structure`
|
|
4
|
+
**Rules:** 5
|
|
5
|
+
|
|
6
|
+
Applies to the organization of `.malloy` files within a package. Skip a file for structural review if it's a `.malloynb` notebook (which has its own conventions) or a one-off sandbox script.
|
|
7
|
+
|
|
8
|
+
For every rule, the linked instruction-skill section is the canonical source for rationale and examples.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## S-01: Base vs joined source separation
|
|
13
|
+
|
|
14
|
+
- **Severity:** major (non-blocking) · **Category:** structure · LLM-judgment
|
|
15
|
+
- **Detection:** LLM heuristic, a source named after a base table (`customers`, `orders`) that includes cross-table joins, or a file with multiple sources where one clearly joins the others, is a candidate for splitting
|
|
16
|
+
- **Fix:** move the joined source into its own file; leave the base source clean
|
|
17
|
+
- **See:** `skill:malloy-model` § Base vs Joined Sources · `skill:malloy-model` § Joined Source File Template
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## S-02: Sources should declare `primary_key:` when one exists
|
|
22
|
+
|
|
23
|
+
- **Severity:** minor (non-blocking) · **Category:** structure · machine-checkable
|
|
24
|
+
- **Why this isn't a correctness rule.** The Malloy compiler auto-synthesizes a UUID-based `__distinct_key` for any `join_one:` target without a declared PK (`packages/malloy/src/model/field_instance.ts:644-670`, `query_query.ts:946-951`), so symmetric aggregation is correct either way, see the C-07 entry in `rubric-correctness.md`'s "Rules we dropped" section. The `with` shortcut, which DOES require a declared PK, errors at compile time and surfaces as a diagnostic. The actual silent-correctness hazard ("declared PK isn't actually unique in the data") is `rubric-correctness.md` § C-12.
|
|
25
|
+
- **What this rule catches.** A discoverability / hygiene gap: when a source has a natural primary key, declaring it lets downstream code use the `with` shortcut, makes grain explicit in the model, and gives tooling a stable identifier per row. Treat as a recommendation, not a merge gate.
|
|
26
|
+
- **Detection:** for every `source:` declaration, check whether its body contains a `primary_key:` clause. Skip flagging when:
|
|
27
|
+
- The source is a query-based / computed source (`source: x is t -> {...}` or `source: x is from(...)`) where grain is determined by the `group_by` columns, declaring a `primary_key:` on the result is fine but not required.
|
|
28
|
+
- The source represents an event/log table or a denormalized analytical source where no natural PK exists. Both situations are legitimate; the LLM should recognize them and skip the finding.
|
|
29
|
+
- **Fix (when the source does have a natural PK):** declare it, `primary_key: <col>` inside `extend {}`. When there isn't a natural PK, leave it undeclared; if the absence is non-obvious, a one-line `#(doc)` on the source explaining the grain helps future readers.
|
|
30
|
+
- **See:** `skill:malloy-model` § Key Rules · `rubric-correctness.md` § C-12 (the related correctness rule that checks whether a declared PK is actually unique in the data) · `rubric-style.md` § Y-03 (`join_one:` style consistency, which is the other consequence of declared-vs-undeclared PKs)
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## S-03: Joined tables defined before the source that references them
|
|
35
|
+
|
|
36
|
+
- **Severity:** blocker (blocking) · **Category:** structure · machine-checkable
|
|
37
|
+
- **Detection:** AST, within a single file, for each source reference inside a `join_*:` or `source is <name>`, verify the target source is declared earlier
|
|
38
|
+
- **Fix:** reorder source declarations so dependencies come first. If two sources reference each other, split into separate files with explicit `import` (cross-file declarations don't have this ordering constraint).
|
|
39
|
+
- **See:** `skill:malloy-gotchas-modeling` § Source Order, Define Joined Tables First
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## S-04: Base sources curate their column surface
|
|
44
|
+
|
|
45
|
+
- **Severity:** major (non-blocking) · **Category:** structure · machine-checkable
|
|
46
|
+
- **Detection:** AST, for each `source: ... is conn.table(...)` declaration (a direct-table base source), check whether the source uses **any** of the three column-curation mechanisms below. The finding fires only when the source uses none of them, every column from the underlying table is implicitly public. Query-based sources and sources derived from other sources are exempt.
|
|
47
|
+
- **Fix:** pick whichever option matches the source's needs. Curation options, lightest to heaviest:
|
|
48
|
+
1. `extend { except: a, b, c }`, drop a small named set of columns. Compatible with `rename:`. No experimental flag.
|
|
49
|
+
2. `extend { accept: a, b, c }`, keep only a small named set. Compatible with `rename:`. No experimental flag.
|
|
50
|
+
3. `##! experimental.access_modifiers` + `include { public: …, internal: …, private: … }`, full per-column visibility tiers. **Incompatible with `rename:`** and disallows measures/dimensions whose names shadow `internal:` columns. Use only when the visibility distinction matters (e.g., shared sources joined into multiple consumers).
|
|
51
|
+
|
|
52
|
+
Most files only need option 1 or 2, for the small set of columns that shouldn't be public, prefer `except:` over the heavier `include {}` machinery. Reach for `include {}` only when per-column tiers are genuinely worth the constraints.
|
|
53
|
+
- **See:** `skill:malloy-gotchas-modeling` § Field Management, `extend {}` vs `include {}` Don't Compose · `skill:malloy-model` § Base Source Templates · `malloy-model/reference/access-modifiers.md`
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## S-05: One file per table (base) / one per analytical domain (joined)
|
|
58
|
+
|
|
59
|
+
- **Severity:** minor (non-blocking) · **Category:** structure · machine-checkable
|
|
60
|
+
- **Detection:** count `source: X is conn.table(...)` base-source declarations per file. More than one → flag.
|
|
61
|
+
- **Fix:** split each base source into its own file named after the source
|
|
62
|
+
- **See:** `skill:malloy-model` § Base Source Templates (file convention is implicit; weak counterpart)
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Cross-cutting note
|
|
67
|
+
|
|
68
|
+
When a file violates both S-01 (base+joined mixed) and S-05 (multiple base sources), surface the combined observation as a **Cross-Cutting Theme** in the review output: "File layout suggests a restructure, see S-series findings for suggested splits."
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Rubric: Style & Naming
|
|
2
|
+
|
|
3
|
+
**Dimension:** `style`
|
|
4
|
+
**Rules:** 3
|
|
5
|
+
|
|
6
|
+
Style findings are almost always `nit` severity, aggregate them in the output rather than listing per-site.
|
|
7
|
+
|
|
8
|
+
For every rule, the linked instruction-skill section is the canonical source for rationale and examples.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Y-01: Prefer semantic aliases over backticked reserved words
|
|
13
|
+
|
|
14
|
+
- **Severity:** minor (non-blocking) · **Category:** style · LLM-judgment
|
|
15
|
+
- **Detection:** LLM, flag any dimension declared as `` `reserved_word` is `reserved_word` `` (the backticked-passthrough pattern) and suggest a semantic alias
|
|
16
|
+
- **Fix:** move the raw column to `internal:` and add a re-aliased dimension with a business name
|
|
17
|
+
- **See:** `skill:malloy-gotchas-modeling` § Reserved Words, Backtick Them
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Y-02: Use business-language naming
|
|
22
|
+
|
|
23
|
+
- **Severity:** nit (non-blocking) · **Category:** style · LLM-judgment
|
|
24
|
+
- **Detection:** LLM judgment informed by, very short names (≤3 chars), hungarian-prefix abbreviations, inconsistent convention within a file. Conventions:
|
|
25
|
+
- **Measures:** suffix with what they measure when helpful (`_count`, `_total`, `_rate`, `_avg`)
|
|
26
|
+
- **Booleans:** prefix with `is_` or `has_`
|
|
27
|
+
- **Timestamps:** suffix with `_at`
|
|
28
|
+
- **Dates (date-only):** suffix with `_date`
|
|
29
|
+
- **IDs:** suffix with `_id`
|
|
30
|
+
- **Avoid:** abbreviations unless they're universal domain terms
|
|
31
|
+
- **Fix:** rename to business language; surface in "Suggested Follow-ups" since these are non-blocking
|
|
32
|
+
- **Note:** strong personal-preference factor, aggregate into "Suggested Follow-ups" rather than emitting per-site, and only when noise rises above project-wide patterns. Don't drown the review in style nits.
|
|
33
|
+
- **See:** your documentation guidance (Writing Doc Strings for Retrieval) (naming-as-discoverability is implicit; weak counterpart, naming conventions are a team-style choice)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Y-03: Use one consistent `join_one:` style across the project
|
|
38
|
+
|
|
39
|
+
- **Severity:** nit (non-blocking) · **Category:** style · LLM-judgment
|
|
40
|
+
- **Detection:** flag a `join_one:` only when it deviates from the project's prevailing style (a single `on` form in a file/package that otherwise uses `with`, or vice versa). Skip when both styles are mixed roughly evenly, or when higher-value findings dominate.
|
|
41
|
+
- **Fix:** match the prevailing style. Switching to `with` requires the target to declare `primary_key:` (also S-02's recommendation).
|
|
42
|
+
- **Never promote above `nit`.** Both forms produce equivalent SQL, see the C-07 entry in `rubric-correctness.md`'s "Rules we dropped" section. The actual silent-correctness hazard (declared PK has duplicates) is `rubric-correctness.md` § C-12, not this rule.
|
|
43
|
+
- **See:** `skill:malloy-model` § Join Syntax
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Aggregation in the output
|
|
48
|
+
|
|
49
|
+
Rather than listing every occurrence inline, roll style findings up in "Suggested Follow-ups":
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
## Suggested Follow-ups (non-blocking)
|
|
53
|
+
|
|
54
|
+
- 34 naming nits aggregated, see Y-series findings in Detailed Findings.
|
|
55
|
+
- Most common: abbreviated measure names (8), missing `is_`/`has_` boolean prefix (6), timestamps without `_at` suffix (5).
|
|
56
|
+
- 3 backticked-passthrough dimensions (Y-01), recommend aliasing to business names.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This keeps the Top Issues section free of noise while still surfacing the pattern for a cleanup pass.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Scope Resolution
|
|
2
|
+
|
|
3
|
+
Every `/malloy-review` invocation must resolve to a **bounded, explicit scope** before any reviewer runs. This file is the single source of truth for how that resolution happens and why.
|
|
4
|
+
|
|
5
|
+
## Why scope is always explicit
|
|
6
|
+
|
|
7
|
+
Malloy repos commonly contain **multiple folders that each target different database connections**. A folder that compiles cleanly against connection A may fail in the context of connection B. Running a review blindly across the whole repo will:
|
|
8
|
+
|
|
9
|
+
1. Produce spurious "cannot resolve column" findings in folders that can't compile without their connection.
|
|
10
|
+
2. Under-count diagnostics because the IDE only has diagnostics for files it's opened against the right connection.
|
|
11
|
+
3. Mislead the user about coverage, the report will claim to have reviewed N files when some of them are in contexts the reviewer couldn't understand.
|
|
12
|
+
|
|
13
|
+
The skill never walks above a scoped root. The review output always echoes the resolved scope so the user can verify coverage at a glance.
|
|
14
|
+
|
|
15
|
+
## Resolution order (first match wins)
|
|
16
|
+
|
|
17
|
+
1. **Positional file argument** (`/malloy-review path/to/file.malloy`), file mode on that file. If the file doesn't exist or isn't `.malloy`, stop.
|
|
18
|
+
2. **Positional directory argument** (`/malloy-review path/to/folder/`), audit mode on that folder. Walks only `.malloy` files within the folder (recursive, but not above it).
|
|
19
|
+
3. **`--pr <n>`**, PR mode. Fetch diff via `gh pr diff <n>`, then intersect with the scope resolved by the rules above (if any). If no other scope was given, the intersected set becomes the scope. See SKILL.md § Mode notes for the PR workflow.
|
|
20
|
+
4. **No argument, CWD is inside a `publisher.json` package**, walk up from CWD until a directory containing `publisher.json` is found. That directory is the package root and the default scope. Review all `.malloy` files at or below it.
|
|
21
|
+
5. **No argument, CWD contains `.malloy` files but no `publisher.json`**, scope is CWD (not recursive above it).
|
|
22
|
+
6. **No argument, CWD is a repo root with multiple `publisher.json` files at child level**, list the packages and ask the user to pick one (or multiple, explicitly). Never auto-fan-out across packages.
|
|
23
|
+
7. **No argument, CWD has no `.malloy` files anywhere nearby**, stop with a helpful error: "No `.malloy` files found at or below this directory. Pass a path or cd into a Malloy project."
|
|
24
|
+
|
|
25
|
+
## Detecting `publisher.json` packages
|
|
26
|
+
|
|
27
|
+
A `publisher.json` file marks a Malloy package boundary. The file doesn't need to be parsed; its presence is the signal.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Walking up from CWD:
|
|
31
|
+
find_package_root() {
|
|
32
|
+
local dir="$PWD"
|
|
33
|
+
while [[ "$dir" != "/" ]]; do
|
|
34
|
+
[[ -f "$dir/publisher.json" ]] && echo "$dir" && return
|
|
35
|
+
dir="$(dirname "$dir")"
|
|
36
|
+
done
|
|
37
|
+
return 1
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Discovering packages below a root (for multi-package disambiguation):
|
|
41
|
+
find "$ROOT" -name publisher.json -not -path '*/node_modules/*' -not -path '*/.git/*'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Multi-package disambiguation
|
|
45
|
+
|
|
46
|
+
When scope resolution hits rule 7 above, present the packages as a lettered list with a one-line summary each:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Multiple Malloy packages found in this repo. Which would you like me to review?
|
|
50
|
+
|
|
51
|
+
(A) packages/finance , 23 .malloy files (.malloy) · 3,412 LOC
|
|
52
|
+
(B) packages/marketing , 11 .malloy files · 1,890 LOC
|
|
53
|
+
(C) packages/shared-joins , 4 .malloy files · 402 LOC
|
|
54
|
+
|
|
55
|
+
You can pick one, several (e.g., "A and C"), or say "all" if you really want cross-package, but note that each package may target a different database connection and findings may conflict.
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Do not default to "all". Wait for the user to pick.
|
|
59
|
+
|
|
60
|
+
## PR-diff intersection
|
|
61
|
+
|
|
62
|
+
When `--pr <n>` is used:
|
|
63
|
+
|
|
64
|
+
1. Fetch diff: `gh pr diff <n> --name-only` → list of changed files.
|
|
65
|
+
2. Filter to `.malloy` files only.
|
|
66
|
+
3. If a scope was also provided (positional path), intersect: `changed_files ∩ scope_files`. Files outside the scope are not reviewed even if they appear in the diff.
|
|
67
|
+
4. If the intersection is empty, stop with a message: "The PR changes no files in the scope you selected. Either drop the scope filter or target a different PR."
|
|
68
|
+
5. The output reports both the PR number **and** the effective scope after intersection.
|
|
69
|
+
|
|
70
|
+
## Emitting scope in the output
|
|
71
|
+
|
|
72
|
+
The `## Scope` section of the review file always lists:
|
|
73
|
+
|
|
74
|
+
- **Reviewed:** the exact list of folders/files/globs that drove the walk.
|
|
75
|
+
- **Package(s):** the `publisher.json` directories covered.
|
|
76
|
+
- **Excluded:** anything skipped (generated dirs, fixtures, files outside the positional path, files outside the intersected PR diff).
|
|
77
|
+
- **File count and LOC total.**
|
|
78
|
+
|
|
79
|
+
The user should be able to read the Scope section and know exactly what was and wasn't reviewed.
|
|
80
|
+
|
|
81
|
+
## Excluded-by-default paths
|
|
82
|
+
|
|
83
|
+
These are skipped unless the user passes a positional path that explicitly includes them:
|
|
84
|
+
|
|
85
|
+
- Anything under `node_modules/`, `.git/`, `.cache/`, `dist/`, `build/`
|
|
86
|
+
- Files named `*.fixture.malloy` or under a `fixtures/` or `__fixtures__/` directory
|
|
87
|
+
- Files under a `tests/` directory where the filename matches `*_test.malloy` or `test_*.malloy`
|
|
88
|
+
- Symlinks (follow cautiously or skip, do not cross filesystem boundaries)
|
|
89
|
+
|
|
90
|
+
These exclusions are always reported in the `## Scope` block's **Excluded** line so the user can see what was skipped.
|
|
91
|
+
|
|
92
|
+
## Stopping the user politely
|
|
93
|
+
|
|
94
|
+
If the skill can't resolve scope (nothing found, ambiguous, wrong file type), stop *before* reading any `.malloy` files. Don't guess, don't default to "the whole repo". The skill should feel conservative: it does exactly what the user asked for and nothing more.
|
|
95
|
+
|
|
96
|
+
Good stop messages name what was tried and offer a concrete fix:
|
|
97
|
+
|
|
98
|
+
> I couldn't find any `.malloy` files at or below `/path/to/cwd`. Run from inside a Malloy package or pass a file or folder as an argument (`/malloy-review path/to/models`).
|
|
99
|
+
|
|
100
|
+
> I found `.malloy` files under three different packages in this repo (`packages/finance`, `packages/marketing`, `packages/shared-joins`). Each may target a different database connection, so I won't fan out automatically. Pick one with `/malloy-review packages/finance/`.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Severity, Confidence, Categorization
|
|
2
|
+
|
|
3
|
+
The shared vocabulary every finding uses. Read this alongside any rubric file.
|
|
4
|
+
|
|
5
|
+
## A finding
|
|
6
|
+
|
|
7
|
+
Every finding emitted during a review has this shape (JSON):
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"id": "C1",
|
|
12
|
+
"severity": "critical",
|
|
13
|
+
"category": "correctness-join",
|
|
14
|
+
"blocking": "blocking",
|
|
15
|
+
"file": "packages/x/customers.malloy",
|
|
16
|
+
"line_range": [12, 12],
|
|
17
|
+
"rule": "C-12 declared primary_key is not unique in the data",
|
|
18
|
+
"current": "primary_key: customer_id (customer_id has duplicates per execute_query check)",
|
|
19
|
+
"expected": "customer_id is unique per row, or the source carries a where: that scopes to a uniquely-keyed subset",
|
|
20
|
+
"suggested_fix": "Pick a different (or composite) PK that IS unique, OR add a source-level where: that makes customer_id unique within the filtered set, OR declare a #(filter) ... required annotation so consumers must supply the discriminating filter.",
|
|
21
|
+
"confidence": 95,
|
|
22
|
+
"evidence": "rubric-correctness C-12; pk_verified=false from SKILL.md step 3 execute_query check",
|
|
23
|
+
"source": "rule"
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`source` is one of:
|
|
28
|
+
- `"rule"`, from a rubric rule applied during the review
|
|
29
|
+
- `"diagnostic"`, promoted from IDE diagnostics (see SKILL.md § Workflow step 2)
|
|
30
|
+
- `"judgment"`, LLM judgment, not tied to a specific rule
|
|
31
|
+
|
|
32
|
+
## Severity scale
|
|
33
|
+
|
|
34
|
+
Five labels, ordered by blast radius:
|
|
35
|
+
|
|
36
|
+
| Severity | What it means | Typical examples |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `blocker` | Code will not compile, run, or produce defined behavior | Syntax error; unbacked reserved word; `run:` without `->` |
|
|
39
|
+
| `critical` | Code compiles but will return silently wrong numbers or bypass governance | Wrong `join_one` vs `join_many` declaration; missing `primary_key:` on a `join_one:` target; method-syntax violation on joined aggregates |
|
|
40
|
+
| `major` | Clear functional or usability problem, not silently wrong but the result is off | `having:` vs `where:` misuse; two aggregates in a chart view; `#(doc)` missing on a public measure |
|
|
41
|
+
| `minor` | Style or consistency issue that affects readability but not correctness | Tag-ordering wrong; one-tag-per-line violation; fixed scale baked into a measure definition |
|
|
42
|
+
| `nit` | Subjective suggestion | Business-language naming preference; praise-adjacent rewrites |
|
|
43
|
+
|
|
44
|
+
## Blocking axis
|
|
45
|
+
|
|
46
|
+
Independent of severity. Answers "should this hold up a merge?"
|
|
47
|
+
|
|
48
|
+
- `blocking`, cannot ship without fixing
|
|
49
|
+
- `non-blocking`, should fix but won't hold up a merge
|
|
50
|
+
- `if-minor`, block only if other work is already touching this area
|
|
51
|
+
|
|
52
|
+
Default mappings (overridable in `.malloy-review.local.md`):
|
|
53
|
+
- `blocker` + `critical` → `blocking`
|
|
54
|
+
- `major` → `blocking` for public-surface / `non-blocking` for internal
|
|
55
|
+
- `minor` → `if-minor`
|
|
56
|
+
- `nit` → `non-blocking`
|
|
57
|
+
|
|
58
|
+
## Category axis
|
|
59
|
+
|
|
60
|
+
A finding's category tells the reader *what kind of problem*, independent of severity:
|
|
61
|
+
|
|
62
|
+
| Category | Covers |
|
|
63
|
+
|---|---|
|
|
64
|
+
| `correctness-syntax` | Parse errors, type errors, unbacked reserved words, `is` vs `as`, missing colons, missing `->` |
|
|
65
|
+
| `correctness-join` | Cardinality declarations, primary-key hygiene, `join_one`/`join_many`/`join_cross` selection, `with`/`on` correctness |
|
|
66
|
+
| `correctness-aggregation` | Symmetric-aggregate locality, method-syntax for joined aggregates, `count()` vs `count(field)`, casts for string aggregates |
|
|
67
|
+
| `correctness-filter` | `where:` vs `having:`, `!= null` vs `is not null`, safe division, alternation-operator precedence |
|
|
68
|
+
| `correctness-type` | Date/time function vs property confusion, boolean-column quoting, `::number` cast misses |
|
|
69
|
+
| `queries` | Chart view multi-aggregate, `order_by` joined-field aliasing, trailing commas, inline measure definitions (DRY) |
|
|
70
|
+
| `docs` | `#(doc)` presence, doc-string quality (no jargon), tag ordering |
|
|
71
|
+
| `rendering` | Scale on measure def (no), `# big_value` missing `# label`, sparkline `# hidden`+`.sparkline=` pair, `# number=id` on years/IDs |
|
|
72
|
+
| `structure` | Base vs joined split, `primary_key:` declared, `include {}` curation on base sources, source-definition order |
|
|
73
|
+
| `governance` | Access modifiers explicit when `##! experimental.access_modifiers` is on, `public: *` softened to a documentation-discipline nudge |
|
|
74
|
+
| `style` | Reserved-word aliases, business-language naming |
|
|
75
|
+
|
|
76
|
+
## Confidence
|
|
77
|
+
|
|
78
|
+
**Confidence is the reviewer's per-finding judgment**, how sure are you that *this specific instance* is a real violation, not a false positive? It's not a property of the rule, and rubric files do not pre-assign it. Every finding carries a 0–100 score; the reviewer assigns it based on the strength of the evidence in the cited code. **Findings with confidence < 80 are dropped** before the review is assembled.
|
|
79
|
+
|
|
80
|
+
Use these as a starting point and adjust per finding:
|
|
81
|
+
|
|
82
|
+
| Evidence source | Starting confidence |
|
|
83
|
+
|---|---|
|
|
84
|
+
| IDE diagnostic, error | 95 |
|
|
85
|
+
| IDE diagnostic, warning | 85 |
|
|
86
|
+
| IDE diagnostic, information / hint | 75 |
|
|
87
|
+
| Live data check (e.g. `execute_query` returns a definitive yes/no, like C-12 PK uniqueness) | 95 |
|
|
88
|
+
| Machine-checkable rubric rule matched against unambiguous code | 90–95 |
|
|
89
|
+
| Machine-checkable rubric rule matched but the context could be misread | 80–90 |
|
|
90
|
+
| LLM-judgment rubric rule with a clearly matching signal (e.g. integer-typed comparison against `ts.month`) | 80–90 |
|
|
91
|
+
| LLM-judgment rubric rule with a softer signal | 70–80 (often falls below threshold and is dropped) |
|
|
92
|
+
| LLM judgment outside any rubric rule, with `rule: "judgment"` | 75–85 |
|
|
93
|
+
|
|
94
|
+
What raises confidence: a live data check that returns the wrong answer; a regex / AST hit that's structurally unambiguous; a finding cross-supported by an IDE diagnostic.
|
|
95
|
+
|
|
96
|
+
What lowers confidence: the rule itself contradicts the Malloy compiler's own warning (e.g. C-01); the detection signal is real but could be deliberate (e.g. a `where:` filter that *looks* like a missing one); the file lacked IDE diagnostic coverage so the compiler-level signal isn't available.
|
|
97
|
+
|
|
98
|
+
When a diagnostic is the only evidence for a finding and re-reading the cited line shows the issue is gone (stale per-open-file diagnostic against a since-fixed file), drop it. Otherwise diagnostics are strong evidence, start above the threshold and usually pass.
|
|
99
|
+
|
|
100
|
+
## Default severity per rule family
|
|
101
|
+
|
|
102
|
+
The rubric files set the default severity per rule. Defaults (can be overridden in `.malloy-review.local.md`):
|
|
103
|
+
|
|
104
|
+
| Rule family | Default severity | Blocking |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| Compile-breaking syntax (parse errors, missing colons / arrows, `as` vs `is`, bare `join:`, reserved-word collisions, redefined query-source columns, trailing commas, etc.) | `blocker` | `blocking`, **handled exclusively by IDE diagnostic pre-pass; not in the rubrics** |
|
|
107
|
+
| Declared `primary_key:` is not actually unique in the data (C-12, verified by `execute_query` in SKILL.md step 3) | `critical` | `blocking` |
|
|
108
|
+
| Chart view with >1 aggregate (Q-01, silent render bug) | `major` | `non-blocking` |
|
|
109
|
+
| `!= null` vs `is not null` (C-01), contradicts the Malloy compiler's own warning, see rule for context | `major` | `non-blocking` |
|
|
110
|
+
| Safe division / boolean-quote (C-03, C-05) | `major` | `blocking` |
|
|
111
|
+
| `#(doc)` missing on public field (D-01) | `major` | `blocking` (public) / `non-blocking` (internal) |
|
|
112
|
+
| One-tag-per-line (D-04) | `minor` | `if-minor` |
|
|
113
|
+
| Base vs joined separation violation (S-01) | `major` | `non-blocking` |
|
|
114
|
+
| `primary_key:` missing on a source (S-02) | `minor` | `non-blocking` |
|
|
115
|
+
| Inconsistent `join_one:` style across project (Y-03) | `nit` | `non-blocking` |
|
|
116
|
+
| Access modifier missing when flag is on (G-02) | `major` | `blocking` (governance) |
|
|
117
|
+
| `public: *` used (G-01, documentation discipline) | `minor` | `non-blocking` |
|
|
118
|
+
| Business-language naming suggestion (Y-02) | `nit` | `non-blocking` |
|
|
119
|
+
|
|
120
|
+
## ID conventions
|
|
121
|
+
|
|
122
|
+
Each finding gets a human-readable ID for cross-reference in the output. The scheme is **severity-letter for top issues, dimension-letter for everything else**:
|
|
123
|
+
|
|
124
|
+
- **Blockers** → `B#` (regardless of dimension; these get top billing)
|
|
125
|
+
- **Criticals** → `C#` (regardless of dimension; these get top billing)
|
|
126
|
+
- **Majors and minors** → per-dimension letter, numbered sequentially within the dimension:
|
|
127
|
+
- `J#` correctness-join
|
|
128
|
+
- `A#` correctness-aggregation
|
|
129
|
+
- `T#` correctness-type (and correctness-syntax)
|
|
130
|
+
- `F#` correctness-filter
|
|
131
|
+
- `Q#` queries
|
|
132
|
+
- `D#` docs
|
|
133
|
+
- `R#` rendering
|
|
134
|
+
- `S#` structure
|
|
135
|
+
- `G#` governance
|
|
136
|
+
- `Y#` style
|
|
137
|
+
- **Nits** → `N#` (regardless of dimension; aggregated under Suggested Follow-ups)
|
|
138
|
+
|
|
139
|
+
Severity is shown in the finding row itself (and in the JSON), so the dimension-letter is enough to identify a finding without conflating it with severity. Numbering restarts at 1 per prefix within a single review file. Cross-references in the Executive Summary / Cross-Cutting Themes use these IDs so a reader can jump to the detailed finding.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: malloy-scope
|
|
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
|
+
---
|
|
5
|
+
|
|
6
|
+
# Propose Analytical Scope
|
|
7
|
+
|
|
8
|
+
**When:** After you have inspected the model and its underlying data. You have read the package's sources and fields and looked at the data distributions.
|
|
9
|
+
|
|
10
|
+
> **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.
|
|
11
|
+
|
|
12
|
+
**Goal:** Present what you found and recommend an analytical focus. The user selects a direction.
|
|
13
|
+
|
|
14
|
+
Ground yourself first with `get_context`: it returns the package's sources, views, and fields, so it tells you what data exists, how it relates, and what is already modeled. Query the data with `execute_query` to get row counts and spot data-quality issues. Keep your proposal and the user's decision in the conversation; there is no separate scope file to write.
|
|
15
|
+
|
|
16
|
+
## What to Present
|
|
17
|
+
|
|
18
|
+
### 1. Table Summary
|
|
19
|
+
|
|
20
|
+
Present a table of the discovered tables with key metadata:
|
|
21
|
+
|
|
22
|
+
| Table | Rows | Columns | Role | Key Relationships |
|
|
23
|
+
|-------|------|---------|------|-------------------|
|
|
24
|
+
| orders | 1.2M | 24 | Fact | FK: customer_id → customers, product_id → products |
|
|
25
|
+
| customers | 50K | 15 | Dimension | PK: customer_id |
|
|
26
|
+
| products | 2K | 12 | Dimension | PK: product_id |
|
|
27
|
+
| order_items | 3.5M | 8 | Bridge | FK: order_id → orders, product_id → products |
|
|
28
|
+
| audit_log | 10M | 6 | Operational | No joins to business tables |
|
|
29
|
+
|
|
30
|
+
Classify each table by role:
|
|
31
|
+
- **Fact**: the events or transactions you measure (orders, sessions, payments).
|
|
32
|
+
- **Dimension**: the entities you slice by (customers, products, regions).
|
|
33
|
+
- **Bridge**: many-to-many linking tables (order_items, tags).
|
|
34
|
+
- **Operational**: ETL, staging, or audit tables that aren't analytical.
|
|
35
|
+
|
|
36
|
+
### 2. Recommended Analytical Focus
|
|
37
|
+
|
|
38
|
+
Identify 2-3 analytical domains: categories of questions the data can answer.
|
|
39
|
+
|
|
40
|
+
- **Order Analysis**: Revenue, order trends, product performance, customer purchasing patterns. (Covers: orders, order_items, products, customers)
|
|
41
|
+
- **Customer Health**: Retention, segmentation, lifetime value, churn risk. (Covers: customers, orders)
|
|
42
|
+
- **Inventory Management**: Stock levels, reorder patterns, supplier performance. (Covers: products, inventory, suppliers)
|
|
43
|
+
|
|
44
|
+
**Recommend one** as the starting point with reasoning:
|
|
45
|
+
|
|
46
|
+
"I'd recommend starting with **Order Analysis**. It covers your most-used tables and the core business questions around revenue and performance. We can add Customer Health as a separate source later."
|
|
47
|
+
|
|
48
|
+
### 3. Tables to Skip
|
|
49
|
+
|
|
50
|
+
Flag tables that shouldn't be modeled (with reasoning):
|
|
51
|
+
|
|
52
|
+
- **audit_log**: Operational/ETL table, not analytical
|
|
53
|
+
- **staging_orders**: Staging table, use `orders` instead
|
|
54
|
+
- **monthly_summary**: Pre-aggregated snapshot, compute fresh in Malloy instead
|
|
55
|
+
|
|
56
|
+
### 4. Scope Options
|
|
57
|
+
|
|
58
|
+
Present 2-3 concrete options as a **numbered list the user can easily select from**. Each option should be a single line with a label, tables included, and key questions it answers. Mark your recommendation.
|
|
59
|
+
|
|
60
|
+
Format choices for easy selection, so the user can reply "A", "B", or "C":
|
|
61
|
+
|
|
62
|
+
**A. Order Analysis (recommended)**: orders + customers + products + order_items. Answers: What's selling? How is revenue trending? Who are the top customers?
|
|
63
|
+
|
|
64
|
+
**B. Full Commerce**: Everything in A plus inventory and suppliers. Broader but more complex.
|
|
65
|
+
|
|
66
|
+
**C. Customer Focus**: customers + orders only. Narrower, focused on retention and segmentation.
|
|
67
|
+
|
|
68
|
+
Pick one (or mix, e.g., "A plus suppliers").
|
|
69
|
+
|
|
70
|
+
## User Interaction
|
|
71
|
+
|
|
72
|
+
**Present choices in a format that's easy to type a short answer to.** Avoid long prose that requires the user to read and synthesize. Numbered/lettered options with one-line descriptions.
|
|
73
|
+
|
|
74
|
+
The user will:
|
|
75
|
+
- **Select** one of the options (or combine them, e.g., "A plus inventory")
|
|
76
|
+
- **Add** tables you missed
|
|
77
|
+
- **Remove** tables they don't want
|
|
78
|
+
- **Redirect** to a different analytical focus entirely
|
|
79
|
+
|
|
80
|
+
## After User Confirms
|
|
81
|
+
|
|
82
|
+
Restate the confirmed scope in the conversation so it's clear what you'll model next. A useful shape to summarize:
|
|
83
|
+
|
|
84
|
+
- **Connection / schema**: the connection name and schema the sources draw from.
|
|
85
|
+
- **Tables in scope**: table, row count, role (Fact/Dimension/Bridge), and any notes.
|
|
86
|
+
- **Analytical focus**: one line describing the analytical domain.
|
|
87
|
+
- **Deferred**: tables left out, with the reason.
|
|
88
|
+
|
|
89
|
+
Then hand off to modeling: use your modeling workflow to turn the confirmed scope into Malloy sources, dimensions, measures, and views.
|
|
90
|
+
|
|
91
|
+
## Tips
|
|
92
|
+
|
|
93
|
+
- **Don't overwhelm**: if there are 20+ tables, group them by domain and focus on the most relevant cluster.
|
|
94
|
+
- **Show evidence**: mention row counts, column counts, relationship density to justify your recommendation.
|
|
95
|
+
- **Be opinionated**: recommend one option clearly, don't present them as equal.
|
|
96
|
+
- **Flag data quality issues** discovered while inspecting the data (e.g., "The `orders` table has ~3% duplicate rows on `order_id`").
|
|
97
|
+
|
|
98
|
+
## Done
|
|
99
|
+
|
|
100
|
+
Scope confirmed in the conversation: tables in scope, analytical focus, and what's deferred. Continue with your modeling workflow.
|