Conversation
malloy-discover's tier-boundaries example used sale_price.percentile(25), and the LookML and Power BI mappings pointed at a percentile function or a raw-SQL fn!() measure. None of those compile. The example now computes exact percentiles in two stages with sum_cumulative, in plain Malloy. malloy-define, malloy-gotchas-modeling and the two mapping tables point to it. Checked against in-memory DuckDB in restricted mode: 1..100 gives p25/p50/p75/p95 of 25/50/75/95, and NULLs must be filtered first. Signed-off-by: James Swirhun <james@credibledata.com>
Malloy returns a model's annotations once per file in its import graph, so a model with six imports that each open with ##! experimental.access_modifiers sent that line six times in every execute_query response. The envelope now keeps the last copy of each repeated line, for model and source annotations. Tags fold last-wins with the importing model last, so keeping the last copy leaves what they resolve to unchanged. Signed-off-by: James Swirhun <james@credibledata.com>
The suggest query already runs `group_by: products.department`, but the package lint only looked at the source's own fields, so every dotted dimension was reported as missing. The lint now walks the path through the source's joins and, when it fails, names the join or field it could not find. Signed-off-by: James Swirhun <james@credibledata.com>
…ides, like REST The query route has includeHiddenFilesAndSources, but the MCP tool did not, so an author working over MCP could not test a file index.malloy hides. execute_query now takes the same optional boolean, default false, and passes it to getQueryResults as the REST route does. The #(authorize) and #(access_filter) gates still apply, and MCP still sends no authorize bypass. Signed-off-by: James Swirhun <james@credibledata.com>
MCP tools answered an unknown environment with "Resource not found: env/conn" and generic suggestions, and dropped the store's own error. getEnvironment now attaches the requested name and the loaded environment names to EnvironmentNotFoundError. classifyToolError, which getModelForQuery now uses too, renders them, so every MCP tool says "Environment 'x' not found. Available environments: ...". The REST 404 message is unchanged. Signed-off-by: James Swirhun <james@credibledata.com>
…f running only the last Malloy runs only the last run: statement of a text, so an ad-hoc query with several returned one answer and silently dropped the rest. Ad-hoc text with more than one run: is now a 400 that names the count, on both the MCP execute_query tool and the REST query route. The count is taken from the compiled query and checked after every boundary and authorize gate, so a request a gate denies keeps the same 404 or 403 as before. Signed-off-by: James Swirhun <james@credibledata.com>
Load and in-place reload each turned the worker's compiled models into live Models and ran validateRenderTags in their own copy of the loop. Both now call Package.hydrateWorkerModels, which differs only in what it does with a model that failed: the load throws, the reload keeps a placeholder. The load's package metadata is built by a named helper. No behavior change. Signed-off-by: James Swirhun <james@credibledata.com>
…ings a reload would A package-scope compile_model ran only the worker compile, so a dashboard that reload_package then flagged (a given annotation that does not parse, a suggest naming a missing field, an unknown render tag) compiled clean. It now runs reload's own hydration and dashboard discovery on a throwaway Package and returns those findings at their own severity, without touching the served package. Build-plan and manifest warnings stay reload-only. Signed-off-by: James Swirhun <james@credibledata.com>
…s reload does Package-scope compile ran the notebook lint over dashboard files and then added the dashboard lint's findings. A dashboard whose `## artifact` tag does not parse was reported by both, while reload reports it once, from the dashboard lint. Both paths now drop the notebook lint's copy through one rule, reportedByDashboardLint. The compile-equals-reload spec now compares the two finding lists in both directions and covers the unparsed tag, so a finding only one side reports, or one side reports twice, fails it. The notebook_lint integration spec expects the package-scope findings this branch added. Two comments that said render-tag findings and dashboard discovery only happen at load are corrected. Signed-off-by: James Swirhun <james@credibledata.com>
execute_query's includeHiddenFilesAndSources lets a caller run what a
package's index.malloy hides. A modeling agent needs that. An agent
answering questions must stay on the curated surface, and Credible's
execute_query has no such parameter. But the eval harness limits its
answerer by tool name, and this is a parameter on a tool the answerer
is allowed, so nothing stopped an answerer from using it. The
off-surface error even told every agent to pass it.
The parameter is now offered only when publisher.config.json sets
"mcp": { "includeHiddenFilesAndSources": true }
The default is off. Off removes it from the tool's schema, and a value
sent anyway is ignored. A non-boolean value is warned about and treated
as off, as a bad theme field is. The off-surface hint that suggested
the flag is gone. REST's parameter of the same name is unchanged.
The setting is a config key rather than an env var because both
launchers that care already write this file: cred dev turns it on, and
the eval's serve.py writes a config without it, so it stays off.
Signed-off-by: James Swirhun <james@credibledata.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
These came from one session of an agent building a model, a dashboard and a data app against a local Publisher (0.8.4) over MCP. Each fix is its own commit, so the PR can be read commit by commit.
The fixes
1. Skills stop teaching a
percentilefunction (fix(skills): ...)Malloy has none. #1249 said so in
malloy-queries, butmalloy-discover's tier-boundaries example still usedsale_price.percentile(25), and the LookML and Power BI mappings pointed at a percentile function or afn!()measure. The example now computes exact percentiles in plain Malloy withsum_cumulative, and the other sites point to it. Checked on DuckDB in restricted mode: 1..100 gives 25/50/75/95. NULLs must be filtered first or every percentile comes out high, and the skill says so.2.
execute_query's_metalists each model annotation once (fix(mcp): ...)Malloy returns a model's annotations once per imported file, so a model with six imports sent
##! experimental.access_modifierssix times in every response. The envelope keeps the last copy of each line. Tags fold last-wins with the importing model last, so the last copy keeps what they resolve to; keeping the first could let an import override the model.3. A dashboard
suggestcan name a joined dimension (fix(dashboards): ...)suggest { source=sales dimension="products.department" }loaded with "that source has no field". The SDK already runs that query correctly; only the load check was too strict. It now walks the source's joins. A bad path names the failing part:"sales" has no join "prodcts". The suggest runs as an ordinary query onsales, so it is gated exactly like the dashboard's own tiles.malloy-dashboardsdocuments the quoted form.4. MCP
execute_querycan takeincludeHiddenFilesAndSources, on an authoring server only (feat(mcp): ...+fix(mcp): offer ... only on an authoring server)#1261/#1264 added it to REST only, so an author curating with
index.malloycouldn't run a hidden file over MCP to test it. MCP now offers the same parameter, but only whenpublisher.config.jsonsets"mcp": { "includeHiddenFilesAndSources": true }. Off is the default. Off removes it from the tool's schema, and a value sent anyway is ignored. That keeps an agent answering questions on the curated surface. Credible'sexecute_queryhas no such parameter, and the eval harness limits its answerer by tool name, so it can't block a parameter. A non-boolean value is warned about and treated as off. It never lifts#(authorize)or#(access_filter), which a test pins. REST's parameter is unchanged. The off-surface error doesn't mention the flag.5. An unknown environment names the ones that exist (
fix(mcp): ...)Every MCP tool answered
environmentName: "analytics"with "Resource not found: analytics/bq_demo" and generic suggestions. It now saysEnvironment 'analytics' not found. Available environments: default. Use a name from list_packages.One shared path inclassifyToolError, whichgetModelForQuerynow uses too. The REST 404 text is unchanged, because a router may scope a REST caller to one environment.6. An ad-hoc query with more than one
run:is a 400 (fix(server): ...)Malloy runs only the last
run:. An agent sent seven and silently got one answer back. MCP and REST now refuse it with the count. Definitions before a singlerun:still work. The count comes from the compiled query and is checked after every boundary and authorize gate, so a request a gate denies keeps the same 404 or 403 and the 400 can't reveal that a hidden source exists.7. Package-scope
compile_modelreports the dashboard and render-tag findings a reload would (refactor(server): ...+fix(compile): ...)compile_modelsaid success, thenreload_packagereported a broken given annotation, a bad suggest field and an unknown render tag. Those checks only ran on reload. The first commit extracts reload's model hydration and render-tag check with no behavior change; the second runs it, plus a dry-run dashboard lint, from package-scope compile. A test checks the findings against a real reload of the same files. Storage, persist and materialization warnings still appear only on reload, and the tool descriptions say so. A follow-up commit (fix(compile): package scope reports an unparsed dashboard tag once) stops compile reporting a dashboard with an unparsable## artifacttag twice; reload reports it once.Decisions for review
query_boundary.spec.tshad asserted that an all-curated multi-statement query "is legitimate and admitted"; it now expects the 400. Decoy cases still get their 404/403. If Malloy ever dropsPreparedQuery.model.queries(), the count is undefined and the check stops refusing;single_run_statement.spec.tswould fail, but it doesn't fail closed at runtime.status. The given and suggest findings are severity error, so a package that compiled "success" and reloaded with warnings now compiles "error". That matches compile's documented rule and the fact that the dashboard control is lost.cred devhas to write"mcp": { "includeHiddenFilesAndSources": true }into its config; until it does, modeling agents there can't run hidden sources over MCP. The eval'sserve.pywrites a config without it, so eval answerers stay curated with no change. It is a config key, not an env var, because both launchers already write this file.How it was checked
On this branch, from
packages/server:bun test ./src: 5036 pass, 36 fail. The 35 config/theme failures fail identically on unmodifiedorigin/mainunder bun 1.4. The 36th was a cross-commit test expectation, fixed in the last commit, and the touched specs now pass 168/168.dashboards,render_tags,mcp,index_convention: 109 pass, 0 fail.bun run lintandtsc --noEmit: clean.bun run test:skills: 330 pass.bun run lint:skills: clean. The skills bundle regenerates with no diff.Each fix was also tested on its own branch before being combined.
For the #4 gate, on the built server (
dist/server.mjs) over the storefront example on DuckDB, with anindex.malloyexporting onlyorder_items:mcpkey:execute_query's schema lists the parameter, and a hidden source and a hidden file both run with it.trueare refused with the normal off-surface error, which doesn't mention the flag.?includeHiddenFilesAndSources=truestill runs the hidden source on the server without the key.execute_query_tool.spec.tspasses 22/22, and the MCP and config specs pass 426/426. Forcing the setting on or off in code fails the matching tests.Left out
compile_modelstill silently uses the lastrun:withincludeSql.get_contextandlist_packagesdon't takeincludeHiddenFilesAndSources, so an author can run a hidden source over MCP but can't search its fields.getModelAnnotations.malloy-chartsandmalloy-queries(analysis group) still say "query the percentiles" without the method, because the group rules stop them referencingmalloy-discover.Supersedes #1271, which held fix 1 alone.