{
"components": [
{
"borrowed_actions": [
"introspect"
],
"brief": "Data access in this deployment goes through the `wren` CLI. Introspect the semantic layer\nwith `wren context show` (and, where available, `wren cube list`) to read models, columns,\nrelationships, and metrics/cubes — do not assume the schema.",
"context_binding": {
"binding_mode": "runtime_selected",
"project": "../jaffle-wren"
},
"context_precondition": [
{
"predicate": "mdl_parseable"
}
],
"context_requirements": [
"a bound semantic context (semantic layer) to introspect"
],
"description": "Survey the bound semantic model and report what can be asked of it — its models, metrics, dimensions and grain — without querying any rows. Use it to orient before analysis, or when someone asks what data is available; it answers questions *about* the model, not questions *from* the data.",
"effect": {
"outcome": {
"kind": "none"
},
"render_blocks": []
},
"entrypoint": true,
"eval": {
"metrics": [
"coverage"
],
"template_ref": "eval/"
},
"eval_ref": "explore_model.eval",
"examples": [
"What's in this dataset?",
"Which metrics and dimensions are available?",
"Can I break revenue down by region?"
],
"guardrails": [
{
"locked": true,
"name": "read_only_execution"
}
],
"id": "explore_model",
"llm_calls": [
{
"conditional": false,
"consumes": [],
"name": "summarize_semantics",
"produces": "semantic_summary",
"prompt": "You introspect the bound semantic context `jaffle-wren` (a semantic layer at `../jaffle-wren`)\nand return a structured summary of what it contains — the map the other GenBI components build on.\n\n- Introspect the semantic layer using the bound introspection capability to read the models,\n columns, relationships, and metrics/cubes. This is the `raw_introspect_result` you consume — read\n it, do not assume the schema.\n- Cover the FULL set: every model, its key columns and their roles (dimension vs measure vs\n time), the relationships between models, and any defined metrics/cubes. Do not drop entries to\n keep the summary short — coverage is the point.\n- Produce `semantic_summary`: a compact structured listing of every model and metric found. Your\n FINAL message must be a single JSON object of the form\n `{\"columns\": [\"model\"], \"rows\": [[\"<model_name>\"], ...]}` listing every model in the layer\n (one row per model), so coverage can be checked deterministically. You may add a short prose\n summary after the JSON.",
"tier": "cheap",
"when": null
}
],
"params": [],
"precondition_result": {
"checks": [
{
"outcome": "pass",
"predicate": "mdl_parseable"
}
],
"status": "pass"
},
"prompt_fragment": "## summarize_semantics\n\nYou introspect the bound semantic context `jaffle-wren` (a semantic layer at `../jaffle-wren`)\nand return a structured summary of what it contains — the map the other GenBI components build on.\n\n- Introspect the semantic layer using the bound introspection capability to read the models,\n columns, relationships, and metrics/cubes. This is the `raw_introspect_result` you consume — read\n it, do not assume the schema.\n- Cover the FULL set: every model, its key columns and their roles (dimension vs measure vs\n time), the relationships between models, and any defined metrics/cubes. Do not drop entries to\n keep the summary short — coverage is the point.\n- Produce `semantic_summary`: a compact structured listing of every model and metric found. Your\n FINAL message must be a single JSON object of the form\n `{\"columns\": [\"model\"], \"rows\": [[\"<model_name>\"], ...]}` listing every model in the layer\n (one row per model), so coverage can be checked deterministically. You may add a short prose\n summary after the JSON.",
"realization_kind": "skill",
"required_capabilities": [
"semantic_introspection",
"llm:cheap"
],
"trigger": {
"kind": "one_shot"
},
"type": "analytical",
"verb": "explore_model"
},
{
"borrowed_actions": [],
"brief": "Data access in this deployment goes through the `wren` CLI. If you need to introspect the\nschema, run `wren context show`. Query the semantic layer with `wren -q -o json -s '<SQL>'`,\nwhich returns JSON; object-shaped rows are also valid — preserve their values exactly. Never\nhand-write SQL against raw tables outside the model.",
"context_binding": {
"binding_mode": "runtime_selected",
"project": "../jaffle-wren"
},
"context_precondition": [
{
"predicate": "mdl_parseable"
}
],
"context_requirements": [
"a bound semantic context (semantic layer) to query"
],
"description": "Answer one natural-language question about the bound semantic model: resolve the intent, generate SQL against the semantic layer, run it read-only, and repair the query if it fails. Returns a result table with the definitions it relied on. Use it for a single question with a single answer, not for a multi-panel overview.",
"effect": {
"outcome": {
"kind": "none"
},
"render_blocks": [
{
"fields": {
"columns": "string[]",
"rows": "row[]"
},
"type": "table"
},
{
"fields": {
"filters": "string[]",
"source_tables": "string[]",
"sql": "string"
},
"type": "definition"
}
]
},
"entrypoint": true,
"eval": {
"metrics": [
"execution_accuracy",
"tier_cost_pareto"
],
"template_ref": "eval/"
},
"eval_ref": "answer_query.eval",
"examples": [
"What were last quarter's top 10 customers by revenue?",
"How many orders shipped late in March?",
"Compare average order value between new and returning customers."
],
"guardrails": [
{
"locked": true,
"name": "read_only_execution"
},
{
"locked": false,
"name": "row_limit",
"threshold": 1000
},
{
"locked": false,
"name": "statement_timeout",
"threshold": 30
},
{
"locked": true,
"name": "deterministic_gate"
}
],
"id": "answer_query",
"llm_calls": [
{
"conditional": false,
"consumes": [],
"name": "resolve_intent",
"produces": "query_intent",
"prompt": "You answer a single data question over the bound semantic context `jaffle-wren` (a semantic\nlayer at `../jaffle-wren`). This first step resolves the user's question into a concrete query intent.\n\n- If unsure of the schema, introspect the semantic context first using the bound introspection\n capability.\n- Identify which model(s), metric(s), dimension(s), filters, grouping, and ordering the question\n implies. Resolve ambiguous business terms to concrete columns/metrics in the semantic layer.\n- Produce `query_intent`: a short, explicit statement of exactly what to compute (measures,\n grouping, filters, ordering, row limit) — enough for the next step to write SQL without\n re-reading the question.",
"tier": "cheap",
"when": null
},
{
"conditional": false,
"consumes": [
"query_intent"
],
"name": "generate_sql",
"produces": "query_result",
"prompt": "Given the resolved `query_intent`, write and execute the query through the semantic layer.\n\n- Query ONLY through the bound query capability, which returns structured (JSON) results. Never\n hand-write SQL against raw tables outside the model — always go through the semantic layer.\n- Respect the guardrails: read-only only, keep within the row limit, and prefer a deterministic\n ordering when the question implies a ranking or a top-N.\n- **Verify the result set (deterministic gate — required before answering).** After the query runs,\n check it is legitimate: it actually executed (no error), it is non-empty where a value is expected,\n the types/units are sane, and the grain matches the question. Record whether it passed.\n- On success, produce `query_result` as exactly one object with this shape and no extra keys:\n ```\n {\"columns\": [\"col1\", ...], \"rows\": [[v1, ...], ...],\n \"summary\": \"<a concise prose answer grounded only in the returned rows>\",\n \"verified\": true,\n \"definition\": {\"sql\": \"<the exact SQL you ran>\", \"source_tables\": [\"...\"], \"filters\": [\"...\"]}}\n ```\n Object-shaped rows are also valid; preserve their values exactly. Emit numbers as numbers.\n Set `verified: true` only after both execution and the deterministic result-set validation pass.\n The summary must state the useful conclusion, not merely describe the columns or claim that the\n query succeeded. `definition` is run-level provenance only; do not invent formal lineage.\n- On failure, keep the attempted SQL, execution/validation evidence, and stable non-secret error in\n `query_result` so the declared repair step can diagnose it. Never mark a failed result verified.",
"tier": "strong",
"when": null
},
{
"conditional": true,
"consumes": [
"query_result"
],
"name": "repair_sql",
"produces": "repaired_result",
"prompt": "Repair step — runs ONLY when `query_result` came back with an execution error or an empty/obviously\nwrong result. If the previous step succeeded with a sensible result, do nothing and pass it through.\n\n- Diagnose the failure from the error text (unknown column, bad join, type mismatch, wrong grain),\n fix the SQL, and re-run it through the bound query capability. Bound your attempts — a few\n retries at most; do not loop indefinitely (retry depth is this step's concern, not the profile's).\n- **If the result still cannot be validated, REFUSE.** Do not fabricate a number. Emit\n `{\"verified\": false, \"refused\": true, \"reason\": \"<why it could not be verified>\"}` and stop.\n- Produce `repaired_result`. On success return exactly the same canonical rich result shape as\n `generate_sql`, with no extra keys:\n ```\n {\"columns\": [\"col1\", ...], \"rows\": [[v1, ...], ...],\n \"summary\": \"<a concise prose answer grounded only in the returned rows>\",\n \"verified\": true,\n \"definition\": {\"sql\": \"<the exact SQL you ran>\", \"source_tables\": [\"...\"], \"filters\": [\"...\"]}}\n ```\n Object-shaped rows are also valid; preserve their values exactly and emit numbers as numbers.\n Set `verified: true` only when the repaired query ran and its result set passed validation. The\n summary must state the useful conclusion grounded only in those rows. The `definition` is\n run-level provenance only (the query behind this answer) — do not invent unit/owner/formal-metric\n lineage (out of scope for this run-level card).",
"tier": "strong",
"when": {
"guard": "on_failure",
"target": "generate_sql"
}
}
],
"params": [],
"precondition_result": {
"checks": [
{
"outcome": "pass",
"predicate": "mdl_parseable"
}
],
"status": "pass"
},
"prompt_fragment": "## resolve_intent\n\nYou answer a single data question over the bound semantic context `jaffle-wren` (a semantic\nlayer at `../jaffle-wren`). This first step resolves the user's question into a concrete query intent.\n\n- If unsure of the schema, introspect the semantic context first using the bound introspection\n capability.\n- Identify which model(s), metric(s), dimension(s), filters, grouping, and ordering the question\n implies. Resolve ambiguous business terms to concrete columns/metrics in the semantic layer.\n- Produce `query_intent`: a short, explicit statement of exactly what to compute (measures,\n grouping, filters, ordering, row limit) — enough for the next step to write SQL without\n re-reading the question.\n\n## generate_sql\n\nGiven the resolved `query_intent`, write and execute the query through the semantic layer.\n\n- Query ONLY through the bound query capability, which returns structured (JSON) results. Never\n hand-write SQL against raw tables outside the model — always go through the semantic layer.\n- Respect the guardrails: read-only only, keep within the row limit, and prefer a deterministic\n ordering when the question implies a ranking or a top-N.\n- **Verify the result set (deterministic gate — required before answering).** After the query runs,\n check it is legitimate: it actually executed (no error), it is non-empty where a value is expected,\n the types/units are sane, and the grain matches the question. Record whether it passed.\n- On success, produce `query_result` as exactly one object with this shape and no extra keys:\n ```\n {\"columns\": [\"col1\", ...], \"rows\": [[v1, ...], ...],\n \"summary\": \"<a concise prose answer grounded only in the returned rows>\",\n \"verified\": true,\n \"definition\": {\"sql\": \"<the exact SQL you ran>\", \"source_tables\": [\"...\"], \"filters\": [\"...\"]}}\n ```\n Object-shaped rows are also valid; preserve their values exactly. Emit numbers as numbers.\n Set `verified: true` only after both execution and the deterministic result-set validation pass.\n The summary must state the useful conclusion, not merely describe the columns or claim that the\n query succeeded. `definition` is run-level provenance only; do not invent formal lineage.\n- On failure, keep the attempted SQL, execution/validation evidence, and stable non-secret error in\n `query_result` so the declared repair step can diagnose it. Never mark a failed result verified.\n\n## repair_sql\n\nRepair step — runs ONLY when `query_result` came back with an execution error or an empty/obviously\nwrong result. If the previous step succeeded with a sensible result, do nothing and pass it through.\n\n- Diagnose the failure from the error text (unknown column, bad join, type mismatch, wrong grain),\n fix the SQL, and re-run it through the bound query capability. Bound your attempts — a few\n retries at most; do not loop indefinitely (retry depth is this step's concern, not the profile's).\n- **If the result still cannot be validated, REFUSE.** Do not fabricate a number. Emit\n `{\"verified\": false, \"refused\": true, \"reason\": \"<why it could not be verified>\"}` and stop.\n- Produce `repaired_result`. On success return exactly the same canonical rich result shape as\n `generate_sql`, with no extra keys:\n ```\n {\"columns\": [\"col1\", ...], \"rows\": [[v1, ...], ...],\n \"summary\": \"<a concise prose answer grounded only in the returned rows>\",\n \"verified\": true,\n \"definition\": {\"sql\": \"<the exact SQL you ran>\", \"source_tables\": [\"...\"], \"filters\": [\"...\"]}}\n ```\n Object-shaped rows are also valid; preserve their values exactly and emit numbers as numbers.\n Set `verified: true` only when the repaired query ran and its result set passed validation. The\n summary must state the useful conclusion grounded only in those rows. The `definition` is\n run-level provenance only (the query behind this answer) — do not invent unit/owner/formal-metric\n lineage (out of scope for this run-level card).",
"realization_kind": "skill",
"required_capabilities": [
"sql_execution:read_only",
"llm:per_step_tier",
"llm:strong",
"llm:cheap"
],
"trigger": {
"kind": "one_shot"
},
"type": "analytical",
"verb": "answer_query"
},
{
"binds": {
"topic_default": "overview"
},
"borrowed_actions": [],
"brief": "Data access in this deployment goes through the `wren` CLI. Discover the schema at query\ntime with `wren context show`, `wren cube list`, and `wren cube describe <cube>` — do not\nassume it. Run each panel query with `wren -q -o json -s '<SQL>'`; every query goes through\n`wren`, never hand-written SQL against raw tables outside the model.",
"context_binding": {
"binding_mode": "runtime_selected",
"project": "../jaffle-wren"
},
"context_precondition": [],
"context_requirements": [
"a bound semantic context (semantic layer) to build dashboards over"
],
"description": "Build a multi-panel dashboard on a topic: plan which panels answer it, run each panel's query read-only, and compose them into one laid-out result of KPI cards, tables and charts. Use it when someone wants an overview of a subject from several angles rather than one specific answer.",
"effect": {
"outcome": {
"kind": "none"
},
"render_blocks": [
{
"fields": {
"delta": "number?",
"label": "string",
"unit": "string?",
"value": "number|string"
},
"type": "kpi_card"
},
{
"fields": {
"columns": "string[]",
"rows": "row[]"
},
"type": "table"
},
{
"fields": {
"chart_type": "bar|line|pie|area|scatter",
"rows": "row[]",
"series": "string[]",
"x": "string"
},
"type": "chart"
},
{
"fields": {
"filters": "string[]",
"source_tables": "string[]",
"sql": "string"
},
"type": "definition"
}
]
},
"entrypoint": true,
"eval": {
"metrics": [
"panel_validity",
"relevance"
],
"template_ref": "eval/"
},
"eval_ref": "generate_dashboard.eval",
"examples": [
"Give me a sales overview for this quarter.",
"Build a dashboard for customer retention.",
"Show me how the marketing funnel is doing."
],
"guardrails": [
{
"locked": true,
"name": "read_only_execution"
},
{
"locked": true,
"name": "artifact_write",
"scope": "."
}
],
"id": "generate_dashboard",
"llm_calls": [
{
"conditional": false,
"consumes": [],
"name": "plan_dashboard",
"produces": "dashboard_plan",
"prompt": "You build data dashboards over the bound semantic context `jaffle-wren` (a semantic layer at\n`../jaffle-wren`).\n\nGiven the user's topic, plan the dashboard:\n\n- If the request doesn't name a fresh topic of its own — it refers back to a dashboard already in\n play (\"the dashboard,\" \"it,\" \"that dashboard\"), or is a bare \"create an artifact for it\" /\n \"build it\" follow-up — do NOT stop here to ask what dashboard or what topic. Take the topic from\n the conversation so far (whatever dashboard/topic was most recently discussed); if there is\n truly none, fall back to an overview of the project's key metrics. Either way, keep planning:\n this step always ends with a `dashboard_plan`, never a clarifying question.\n- Discover available models, columns, and cubes **at query time** using the bound introspection\n capability. Do not assume the schema — introspect it.\n- Decide which metrics and dimensions answer the topic, and what panels are needed\n (KPI cards for headline numbers, a chart for trends/breakdowns, a table for detail).\n- Produce `dashboard_plan`: for each panel, the panel type (kpi_card | table | chart) and the exact\n query (through the semantic layer) that populates it. Every query goes through the bound query\n capability; never hand-write SQL against raw tables outside the model.",
"tier": "strong",
"when": null
},
{
"conditional": false,
"consumes": [
"dashboard_plan"
],
"name": "compose_layout",
"produces": "dashboard",
"prompt": "Given the `dashboard_plan`, run each panel's query and compose the dashboard.\n\n- Run each planned panel query through the bound query capability and collect the results — these\n are the `panel_results` you consume. (This inlines the query behavior; it does not call a\n separate query component.)\n- Assemble each panel into a typed render block conforming to the render contract:\n `kpi_card` for headline numbers, `chart` for trends/breakdowns, `table` for detail.\n- Produce `dashboard`: follow the \"Render output\" instructions the dispatcher appends below for the\n active render flavor — emit the `{ blocks, summary }` envelope (programmatic) or write the HTML\n (prompt). The blocks must carry real values from `panel_results`, not placeholders.\n- The rendered dashboard IS the artifact. Never ask the user what kind of artifact they want, and\n never offer alternatives — saving the plan/JSON to a file, exporting to CSV, \"something else?\",\n etc. Whatever the user called it (\"an artifact,\" \"a report,\" \"the dashboard\"), this step's only\n job is to compose it and produce `dashboard` per the render flavor above; don't stop to\n clarify format, and don't do anything else instead (no writing files outside what \"Render\n output\" already directs).",
"tier": "cheap",
"when": null
}
],
"params": [
{
"bind": "optional",
"default": "overview",
"name": "topic_default"
}
],
"precondition_result": {
"checks": [],
"status": "pass"
},
"prompt_fragment": "## plan_dashboard\n\nYou build data dashboards over the bound semantic context `jaffle-wren` (a semantic layer at\n`../jaffle-wren`).\n\nGiven the user's topic, plan the dashboard:\n\n- If the request doesn't name a fresh topic of its own — it refers back to a dashboard already in\n play (\"the dashboard,\" \"it,\" \"that dashboard\"), or is a bare \"create an artifact for it\" /\n \"build it\" follow-up — do NOT stop here to ask what dashboard or what topic. Take the topic from\n the conversation so far (whatever dashboard/topic was most recently discussed); if there is\n truly none, fall back to an overview of the project's key metrics. Either way, keep planning:\n this step always ends with a `dashboard_plan`, never a clarifying question.\n- Discover available models, columns, and cubes **at query time** using the bound introspection\n capability. Do not assume the schema — introspect it.\n- Decide which metrics and dimensions answer the topic, and what panels are needed\n (KPI cards for headline numbers, a chart for trends/breakdowns, a table for detail).\n- Produce `dashboard_plan`: for each panel, the panel type (kpi_card | table | chart) and the exact\n query (through the semantic layer) that populates it. Every query goes through the bound query\n capability; never hand-write SQL against raw tables outside the model.\n\n## compose_layout\n\nGiven the `dashboard_plan`, run each panel's query and compose the dashboard.\n\n- Run each planned panel query through the bound query capability and collect the results — these\n are the `panel_results` you consume. (This inlines the query behavior; it does not call a\n separate query component.)\n- Assemble each panel into a typed render block conforming to the render contract:\n `kpi_card` for headline numbers, `chart` for trends/breakdowns, `table` for detail.\n- Produce `dashboard`: follow the \"Render output\" instructions the dispatcher appends below for the\n active render flavor — emit the `{ blocks, summary }` envelope (programmatic) or write the HTML\n (prompt). The blocks must carry real values from `panel_results`, not placeholders.\n- The rendered dashboard IS the artifact. Never ask the user what kind of artifact they want, and\n never offer alternatives — saving the plan/JSON to a file, exporting to CSV, \"something else?\",\n etc. Whatever the user called it (\"an artifact,\" \"a report,\" \"the dashboard\"), this step's only\n job is to compose it and produce `dashboard` per the render flavor above; don't stop to\n clarify format, and don't do anything else instead (no writing files outside what \"Render\n output\" already directs).",
"realization_kind": "skill",
"required_capabilities": [
"sql_execution:read_only",
"genbi_build",
"render_contract",
"artifact_write",
"llm:per_step_tier",
"llm:strong",
"llm:cheap"
],
"trigger": {
"kind": "one_shot"
},
"type": "analytical",
"verb": "generate_dashboard"
},
{
"borrowed_actions": [],
"brief": "Data access in this deployment goes through the `wren` CLI. Introspect the layer as needed\nwith `wren context show`. Run the decomposition queries with `wren -q -o json -s '<SQL>'`.",
"context_binding": {
"binding_mode": "runtime_selected",
"project": "../jaffle-wren"
},
"context_precondition": [],
"context_requirements": [
"a bound semantic context with an additive metric plus a time dimension and a groupable dimension"
],
"description": "Explain why a metric moved: decompose the change across time and the dimensions that drive it, then report the contributing drivers as a narrative. Needs an additive metric with a time dimension; the specific metric's additivity is checked at run time. Use it for causal \"why did this move\" questions, not for retrieving the number itself.",
"effect": {
"outcome": {
"kind": "none"
},
"render_blocks": [
{
"fields": {
"text": "string",
"title": "string?"
},
"type": "narrative"
}
]
},
"entrypoint": true,
"eval": {
"metrics": [
"driver_correctness",
"additivity_guard"
],
"template_ref": "eval/"
},
"eval_ref": "explain_change.eval",
"examples": [
"Why did revenue drop last month?",
"What's driving the increase in churn this quarter?",
"Which regions explain the spike in refunds?"
],
"guardrails": [
{
"locked": true,
"name": "read_only_execution"
},
{
"locked": false,
"name": "drill_depth_limit",
"threshold": 3
},
{
"locked": true,
"name": "additivity_guard"
},
{
"locked": true,
"name": "artifact_write",
"scope": "."
}
],
"id": "explain_change",
"llm_calls": [
{
"conditional": false,
"consumes": [],
"name": "plan_decomposition",
"produces": "decomposition_plan",
"prompt": "You explain why a metric changed over the bound semantic context `jaffle-wren` (a semantic\nlayer at `../jaffle-wren`), by planning how to decompose the change.\n\n- Introspect the layer as needed, using the bound introspection capability, to find the metric, its\n time dimension, and the dimensions you can break the change down by.\n- Confirm the metric is **additive** across the dimensions you intend to decompose along (a sum of\n parts equals the whole). Nothing upstream guarantees this — you must check that the **specific**\n metric you decompose is additive. If it is a ratio/average/distinct-count or otherwise\n non-additive, note that: decomposing it can mislead.\n- Produce `decomposition_plan`: the metric, the two periods being compared, and the ordered list of\n dimensions to decompose the delta along (bounded by the drill-depth limit).",
"tier": "strong",
"when": null
},
{
"conditional": false,
"consumes": [
"decomposition_plan"
],
"name": "synthesize_drivers",
"produces": "driver_explanation",
"prompt": "Given the `decomposition_plan`, quantify the change and synthesize the drivers into an explanation.\n\n- Run the decomposition queries through the bound query capability: compute the metric for each\n period and the per-dimension contribution to the delta. Rank contributors by the size of their\n contribution.\n- Produce `driver_explanation` as a `narrative` render block: a short, ordered account of what drove\n the change (largest contributors first), with the actual numbers. Follow the \"Render output\"\n instructions the dispatcher appends below.\n- **Additivity caveat (required):** if the metric is not strictly additive across the decomposition\n dimensions, state plainly in the narrative that the attribution is approximate and additivity was\n not enforced. Never present a decomposition of a non-additive metric as exact — the hero output\n must not claim to run on verified reasoning it did not.",
"tier": "strong",
"when": null
}
],
"params": [],
"precondition_result": {
"checks": [],
"status": "pass"
},
"prompt_fragment": "## plan_decomposition\n\nYou explain why a metric changed over the bound semantic context `jaffle-wren` (a semantic\nlayer at `../jaffle-wren`), by planning how to decompose the change.\n\n- Introspect the layer as needed, using the bound introspection capability, to find the metric, its\n time dimension, and the dimensions you can break the change down by.\n- Confirm the metric is **additive** across the dimensions you intend to decompose along (a sum of\n parts equals the whole). Nothing upstream guarantees this — you must check that the **specific**\n metric you decompose is additive. If it is a ratio/average/distinct-count or otherwise\n non-additive, note that: decomposing it can mislead.\n- Produce `decomposition_plan`: the metric, the two periods being compared, and the ordered list of\n dimensions to decompose the delta along (bounded by the drill-depth limit).\n\n## synthesize_drivers\n\nGiven the `decomposition_plan`, quantify the change and synthesize the drivers into an explanation.\n\n- Run the decomposition queries through the bound query capability: compute the metric for each\n period and the per-dimension contribution to the delta. Rank contributors by the size of their\n contribution.\n- Produce `driver_explanation` as a `narrative` render block: a short, ordered account of what drove\n the change (largest contributors first), with the actual numbers. Follow the \"Render output\"\n instructions the dispatcher appends below.\n- **Additivity caveat (required):** if the metric is not strictly additive across the decomposition\n dimensions, state plainly in the narrative that the attribution is approximate and additivity was\n not enforced. Never present a decomposition of a non-additive metric as exact — the hero output\n must not claim to run on verified reasoning it did not.",
"realization_kind": "skill",
"required_capabilities": [
"sql_execution:read_only",
"render_contract",
"artifact_write",
"llm:strong"
],
"trigger": {
"kind": "one_shot"
},
"type": "analytical",
"verb": "explain_change"
}
],
"config": {},
"context_binding": {
"binding_mode": "runtime_selected",
"project": "../jaffle-wren",
"resolved": {
"dimensions": [
{
"name": "status",
"temporal": false
},
{
"name": "order_date",
"temporal": true
},
{
"name": "first_name",
"temporal": false
},
{
"name": "last_name",
"temporal": false
},
{
"name": "first_order",
"temporal": true
},
{
"name": "most_recent_order",
"temporal": true
},
{
"name": "name",
"temporal": false
},
{
"name": "order_date",
"temporal": true
},
{
"name": "status",
"temporal": false
},
{
"name": "first_name",
"temporal": false
},
{
"name": "last_name",
"temporal": false
},
{
"name": "order_date",
"temporal": true
},
{
"name": "status",
"temporal": false
},
{
"name": "payment_method",
"temporal": false
}
],
"lineage": {
"edges": 12,
"nodes": 15,
"resolvable": true
},
"metrics": [
{
"additivity": "additive",
"declared": true,
"name": "total_revenue"
},
{
"additivity": "non_additive",
"declared": true,
"name": "avg_order_value"
},
{
"declared": false,
"name": "customer_id"
},
{
"declared": false,
"name": "number_of_orders"
},
{
"declared": false,
"name": "customer_lifetime_value"
},
{
"declared": false,
"name": "id"
},
{
"declared": false,
"name": "order_id"
},
{
"declared": false,
"name": "customer_id"
},
{
"declared": false,
"name": "credit_card_amount"
},
{
"declared": false,
"name": "coupon_amount"
},
{
"declared": false,
"name": "bank_transfer_amount"
},
{
"declared": false,
"name": "gift_card_amount"
},
{
"declared": false,
"name": "amount"
},
{
"declared": false,
"name": "id"
},
{
"declared": false,
"name": "id"
},
{
"declared": false,
"name": "user_id"
},
{
"declared": false,
"name": "id"
},
{
"declared": false,
"name": "order_id"
},
{
"declared": false,
"name": "amount"
}
],
"models": [
"customers",
"example",
"orders",
"raw_customers",
"raw_orders",
"raw_payments"
],
"time_dimensions": [
"order_date",
"first_order",
"most_recent_order",
"order_date",
"order_date"
]
}
},
"profile": "analysis-agent",
"warble_ir_version": "0.9"
}