@wrenai/genbi 0.0.4 → 0.0.6

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.
Files changed (234) hide show
  1. package/README.md +65 -3
  2. package/dist/assets/{echarts-LTFC-ysd.js → echarts-DYM4hjvk.js} +2 -2
  3. package/dist/assets/index-A_Wrq7Ey.css +1 -0
  4. package/dist/assets/{index-Bo--QMUN.js → index-DsH129tV.js} +18 -17
  5. package/dist/index.html +2 -2
  6. package/dist-server/harness/bundle/schema.d.ts +38 -0
  7. package/dist-server/harness/bundle/schema.js +18 -10
  8. package/dist-server/harness/bundle/version.js +1 -1
  9. package/dist-server/harness/cli-args.d.ts +10 -2
  10. package/dist-server/harness/cli-args.js +24 -8
  11. package/dist-server/harness/cli.js +75 -9
  12. package/dist-server/harness/compile/cache.js +4 -4
  13. package/dist-server/harness/compile/compose-profile.d.ts +51 -14
  14. package/dist-server/harness/compile/compose-profile.js +85 -19
  15. package/dist-server/harness/compile/context-loader.d.ts +56 -0
  16. package/dist-server/harness/compile/context-loader.js +237 -0
  17. package/dist-server/harness/compile/errors.d.ts +19 -0
  18. package/dist-server/harness/compile/errors.js +35 -0
  19. package/dist-server/harness/compile/index.d.ts +4 -3
  20. package/dist-server/harness/compile/index.js +4 -3
  21. package/dist-server/harness/compile/pipeline.d.ts +15 -7
  22. package/dist-server/harness/compile/pipeline.js +62 -14
  23. package/dist-server/harness/compile/resolve-binary.d.ts +2 -2
  24. package/dist-server/harness/compile/resolve-binary.js +3 -3
  25. package/dist-server/harness/compile/types.d.ts +29 -0
  26. package/dist-server/harness/compile/warble-identity.d.ts +11 -0
  27. package/dist-server/harness/compile/warble-identity.js +13 -0
  28. package/dist-server/harness/components/ai-step.d.ts +4 -0
  29. package/dist-server/harness/components/ai-step.js +40 -0
  30. package/dist-server/harness/components/broker.d.ts +60 -0
  31. package/dist-server/harness/components/broker.js +117 -0
  32. package/dist-server/harness/components/capability-card.d.ts +98 -0
  33. package/dist-server/harness/components/capability-card.js +169 -0
  34. package/dist-server/harness/components/context.d.ts +2 -0
  35. package/dist-server/harness/components/context.js +30 -0
  36. package/dist-server/harness/components/display.d.ts +6 -0
  37. package/dist-server/harness/components/display.js +29 -0
  38. package/dist-server/harness/components/egress-judge.d.ts +12 -0
  39. package/dist-server/harness/components/egress-judge.js +27 -0
  40. package/dist-server/harness/components/egress.d.ts +142 -0
  41. package/dist-server/harness/components/egress.js +469 -0
  42. package/dist-server/harness/components/in-process.d.ts +7 -0
  43. package/dist-server/harness/components/in-process.js +245 -0
  44. package/dist-server/harness/components/normalize.d.ts +35 -0
  45. package/dist-server/harness/components/normalize.js +245 -0
  46. package/dist-server/harness/components/plan.d.ts +31 -0
  47. package/dist-server/harness/components/plan.js +184 -0
  48. package/dist-server/harness/components/report.d.ts +207 -0
  49. package/dist-server/harness/components/report.js +309 -0
  50. package/dist-server/harness/components/runner.d.ts +193 -0
  51. package/dist-server/harness/components/runner.js +492 -0
  52. package/dist-server/harness/components/semantic-guards.d.ts +20 -0
  53. package/dist-server/harness/components/semantic-guards.js +42 -0
  54. package/dist-server/harness/components/terminal-json.d.ts +18 -0
  55. package/dist-server/harness/components/terminal-json.js +82 -0
  56. package/dist-server/harness/components/usage.d.ts +14 -0
  57. package/dist-server/harness/components/usage.js +19 -0
  58. package/dist-server/harness/components/wren-access.d.ts +29 -0
  59. package/dist-server/harness/components/wren-access.js +233 -0
  60. package/dist-server/harness/components/zone-dry-run.d.ts +43 -0
  61. package/dist-server/harness/components/zone-dry-run.js +107 -0
  62. package/dist-server/harness/components/zone-gate.d.ts +76 -0
  63. package/dist-server/harness/components/zone-gate.js +144 -0
  64. package/dist-server/harness/events/index.d.ts +1 -1
  65. package/dist-server/harness/events/types.d.ts +52 -0
  66. package/dist-server/harness/index.d.ts +9 -2
  67. package/dist-server/harness/index.js +5 -1
  68. package/dist-server/harness/loop/executor.d.ts +1 -0
  69. package/dist-server/harness/loop/executor.js +37 -15
  70. package/dist-server/harness/npm-package-resolve.d.ts +2 -0
  71. package/dist-server/harness/npm-package-resolve.js +16 -4
  72. package/dist-server/harness/providers/adapters/openai-compatible.d.ts +13 -0
  73. package/dist-server/harness/providers/adapters/openai-compatible.js +17 -0
  74. package/dist-server/harness/providers/adapters/openai.d.ts +7 -0
  75. package/dist-server/harness/providers/adapters/openai.js +6 -0
  76. package/dist-server/harness/providers/adapters/pi-ai.d.ts +48 -0
  77. package/dist-server/harness/providers/adapters/pi-ai.js +518 -0
  78. package/dist-server/harness/providers/binding.d.ts +45 -4
  79. package/dist-server/harness/providers/binding.js +44 -8
  80. package/dist-server/harness/providers/index.d.ts +9 -3
  81. package/dist-server/harness/providers/index.js +10 -3
  82. package/dist-server/harness/providers/zone.d.ts +96 -0
  83. package/dist-server/harness/providers/zone.js +112 -0
  84. package/dist-server/harness/render/answering-query.d.ts +29 -0
  85. package/dist-server/harness/render/answering-query.js +62 -0
  86. package/dist-server/harness/render/envelope.d.ts +6 -0
  87. package/dist-server/harness/render/envelope.js +10 -3
  88. package/dist-server/harness/route/adapter-spec.d.ts +8 -9
  89. package/dist-server/harness/route/adapter-spec.js +13 -14
  90. package/dist-server/harness/route/codex-ask-events.js +2 -1
  91. package/dist-server/harness/route/codex-ask.d.ts +4 -0
  92. package/dist-server/harness/route/codex-ask.js +43 -45
  93. package/dist-server/harness/route/codex-local-manifest.d.ts +9 -1
  94. package/dist-server/harness/route/codex-local-manifest.js +96 -15
  95. package/dist-server/harness/route/describe.js +45 -6
  96. package/dist-server/harness/route/dispatched.d.ts +20 -0
  97. package/dist-server/harness/route/dispatched.js +122 -24
  98. package/dist-server/harness/route/host-query.d.ts +93 -0
  99. package/dist-server/harness/route/host-query.js +257 -0
  100. package/dist-server/harness/route/in-process.d.ts +22 -1
  101. package/dist-server/harness/route/in-process.js +54 -4
  102. package/dist-server/harness/route/route.js +8 -2
  103. package/dist-server/harness/route/types.d.ts +47 -1
  104. package/dist-server/harness/route/wren-host-proxy.d.ts +33 -0
  105. package/dist-server/harness/route/wren-host-proxy.js +228 -0
  106. package/dist-server/harness/session/run.js +30 -10
  107. package/dist-server/harness/setup/runner.d.ts +17 -3
  108. package/dist-server/harness/setup/runner.js +272 -89
  109. package/dist-server/harness/setup/yaml-scalar.d.ts +23 -0
  110. package/dist-server/harness/setup/yaml-scalar.js +62 -0
  111. package/dist-server/harness/tools/setup-native.d.ts +7 -0
  112. package/dist-server/harness/tools/setup-native.js +7 -4
  113. package/dist-server/server/adopt.js +1 -17
  114. package/dist-server/server/app.d.ts +1 -0
  115. package/dist-server/server/app.js +175 -17
  116. package/dist-server/server/auth-choice.d.ts +13 -1
  117. package/dist-server/server/auth-choice.js +66 -1
  118. package/dist-server/server/bin.js +185 -25
  119. package/dist-server/server/codex-native-session.d.ts +25 -0
  120. package/dist-server/server/codex-native-session.js +1 -0
  121. package/dist-server/server/codex-runtime-composition.d.ts +34 -0
  122. package/dist-server/server/codex-runtime-composition.js +250 -0
  123. package/dist-server/server/codex-structured-ask.d.ts +23 -0
  124. package/dist-server/server/codex-structured-ask.js +102 -0
  125. package/dist-server/server/conn-config.js +4 -16
  126. package/dist-server/server/conversation-socket.d.ts +16 -0
  127. package/dist-server/server/conversation-socket.js +65 -0
  128. package/dist-server/server/db.d.ts +46 -0
  129. package/dist-server/server/db.js +88 -9
  130. package/dist-server/server/env-detect.d.ts +6 -0
  131. package/dist-server/server/env-detect.js +7 -0
  132. package/dist-server/server/fold.d.ts +23 -4
  133. package/dist-server/server/fold.js +42 -5
  134. package/dist-server/server/harness.d.ts +2 -2
  135. package/dist-server/server/harness.js +33 -7
  136. package/dist-server/server/host-evidence-grounding.d.ts +21 -0
  137. package/dist-server/server/host-evidence-grounding.js +93 -0
  138. package/dist-server/server/interactive-terminal.d.ts +10 -6
  139. package/dist-server/server/interactive-terminal.js +33 -8
  140. package/dist-server/server/managed-wren-runtime.d.ts +116 -0
  141. package/dist-server/server/managed-wren-runtime.js +670 -0
  142. package/dist-server/server/native-artifacts.d.ts +112 -7
  143. package/dist-server/server/native-artifacts.js +194 -14
  144. package/dist-server/server/native-component-context.d.ts +19 -0
  145. package/dist-server/server/native-component-context.js +76 -0
  146. package/dist-server/server/native-component-preparation.d.ts +20 -0
  147. package/dist-server/server/native-component-preparation.js +129 -0
  148. package/dist-server/server/native-component-runtime.d.ts +36 -0
  149. package/dist-server/server/native-component-runtime.js +92 -0
  150. package/dist-server/server/native-components.d.ts +84 -0
  151. package/dist-server/server/native-components.js +160 -0
  152. package/dist-server/server/native-dispatch-process.d.ts +8 -0
  153. package/dist-server/server/native-dispatch-process.js +65 -0
  154. package/dist-server/server/native-runtime-spec.d.ts +89 -0
  155. package/dist-server/server/native-runtime-spec.js +169 -0
  156. package/dist-server/server/native-sessions.d.ts +172 -19
  157. package/dist-server/server/native-sessions.js +839 -244
  158. package/dist-server/server/native-shutdown.d.ts +6 -0
  159. package/dist-server/server/native-shutdown.js +28 -0
  160. package/dist-server/server/native-wren-home.d.ts +18 -0
  161. package/dist-server/server/native-wren-home.js +262 -0
  162. package/dist-server/server/node-pty-host.d.ts +2 -1
  163. package/dist-server/server/node-pty-host.js +3 -2
  164. package/dist-server/server/profile-registry.d.ts +139 -0
  165. package/dist-server/server/profile-registry.js +620 -0
  166. package/dist-server/server/runtime-binding.d.ts +4 -1
  167. package/dist-server/server/runtime-binding.js +50 -2
  168. package/dist-server/server/runtime-host/claude-component-step.d.ts +25 -0
  169. package/dist-server/server/runtime-host/claude-component-step.js +186 -0
  170. package/dist-server/server/runtime-host/codex-app-server.d.ts +75 -0
  171. package/dist-server/server/runtime-host/codex-app-server.js +239 -0
  172. package/dist-server/server/runtime-host/codex-compatibility.d.ts +110 -0
  173. package/dist-server/server/runtime-host/codex-compatibility.js +162 -0
  174. package/dist-server/server/runtime-host/codex-component-step.d.ts +19 -0
  175. package/dist-server/server/runtime-host/codex-component-step.js +308 -0
  176. package/dist-server/server/runtime-host/codex-conversation.d.ts +109 -0
  177. package/dist-server/server/runtime-host/codex-conversation.js +295 -0
  178. package/dist-server/server/runtime-host/codex-direct-tools.d.ts +23 -0
  179. package/dist-server/server/runtime-host/codex-direct-tools.js +59 -0
  180. package/dist-server/server/runtime-host/codex-events.d.ts +647 -0
  181. package/dist-server/server/runtime-host/codex-events.js +66 -0
  182. package/dist-server/server/runtime-host/codex-policy.d.ts +16 -0
  183. package/dist-server/server/runtime-host/codex-policy.js +151 -0
  184. package/dist-server/server/runtime-host/codex-process.d.ts +8 -0
  185. package/dist-server/server/runtime-host/codex-process.js +96 -0
  186. package/dist-server/server/runtime-host/codex-rpc.d.ts +47 -0
  187. package/dist-server/server/runtime-host/codex-rpc.js +182 -0
  188. package/dist-server/server/runtime-host/codex-session.d.ts +74 -0
  189. package/dist-server/server/runtime-host/codex-session.js +492 -0
  190. package/dist-server/server/runtime-host/codex-terminal-protocol.d.ts +35 -0
  191. package/dist-server/server/runtime-host/codex-terminal-protocol.js +243 -0
  192. package/dist-server/server/runtime-host/codex-terminal.d.ts +16 -0
  193. package/dist-server/server/runtime-host/codex-terminal.js +120 -0
  194. package/dist-server/server/runtime-host/index.d.ts +5 -0
  195. package/dist-server/server/runtime-host/index.js +3 -0
  196. package/dist-server/server/runtime-host/local.d.ts +29 -0
  197. package/dist-server/server/runtime-host/local.js +90 -0
  198. package/dist-server/server/runtime-host/policy.d.ts +8 -0
  199. package/dist-server/server/runtime-host/policy.js +78 -0
  200. package/dist-server/server/runtime-host/types.d.ts +52 -0
  201. package/dist-server/server/runtime-host/types.js +62 -0
  202. package/dist-server/server/structured-query-evidence.d.ts +7 -0
  203. package/dist-server/server/structured-query-evidence.js +47 -0
  204. package/dist-server/server/structured-runtime.d.ts +52 -0
  205. package/dist-server/server/structured-runtime.js +130 -0
  206. package/dist-server/server/structured-scope.d.ts +4 -0
  207. package/dist-server/server/structured-scope.js +12 -0
  208. package/dist-server/server/turn.d.ts +6 -1
  209. package/dist-server/server/turn.js +86 -26
  210. package/dist-server/server/wire-types.d.ts +67 -3
  211. package/dist-server/server/wren-profiles.js +1 -1
  212. package/managed-wren/README.md +60 -0
  213. package/managed-wren/dependency-wheels.json +282 -0
  214. package/managed-wren/manifest.json +373 -0
  215. package/managed-wren/release-inputs.json +45 -0
  216. package/managed-wren/runtime-tree.cjs +115 -0
  217. package/managed-wren/source-inputs.json +40 -0
  218. package/package.json +15 -6
  219. package/profiles/genbi-default/context/binding.yml +17 -7
  220. package/profiles/genbi-default/context/context.json +146 -0
  221. package/profiles/genbi-default/ir.golden.json +39 -43
  222. package/profiles/genbi-default/profile.yml +31 -0
  223. package/profiles/genbi-enrich-context/context/binding.yml +14 -3
  224. package/profiles/genbi-enrich-context/context/context.json +146 -0
  225. package/profiles/genbi-enrich-context/ir.golden.json +4 -5
  226. package/profiles/genbi-monitor/context/binding.yml +13 -5
  227. package/profiles/genbi-monitor/context/context.json +146 -0
  228. package/profiles/genbi-monitor/ir.golden.json +13 -7
  229. package/profiles/genbi-report/context/binding.yml +19 -0
  230. package/profiles/genbi-report/context/context.json +223 -0
  231. package/profiles/genbi-report/ir.golden.json +328 -0
  232. package/profiles/genbi-report/profile.yml +38 -0
  233. package/profiles/genbi-setup/ir.golden.json +3 -1
  234. package/dist/assets/index-Xs4q7pNo.css +0 -1
@@ -1,9 +1,19 @@
1
- # Coarse context binding: the whole v5-jaffle semantic layer. Since IR v0.3 the Warble front-end
2
- # builds a real MdlContext over the bound project and evaluates context_precondition predicates
3
- # against it — it does introspect MDL. The binding stays "coarse" in a different sense: it names the
4
- # whole project, not a specific metric/grain.
1
+ # Coarse context binding: the whole v5-jaffle semantic layer, bound as a PREPARED document.
5
2
  #
6
- # The host rewrites this `project:` line to the user's own wren project at bind time; the committed
7
- # value pins this repo's own v5 example so the profile compiles standalone for CI/dev.
8
- # Path is relative to this profile directory.
3
+ # `kind: prepared` means Warble does not read the semantic layer itself — it reads the JSON document
4
+ # `document:` names, which WrenAI's own `wren-context-loader` generator produced from the project.
5
+ # Warble therefore needs no knowledge of MDL, and the projection Wren hands it is the one Wren
6
+ # controls. The binding stays "coarse" in the original sense: it names the whole project, not a
7
+ # specific metric/grain.
8
+ #
9
+ # `project:` is the layer's IDENTITY, not a path Warble reads, and never the document's path — it is
10
+ # what the compiled prompts call the project the agent is working on. `document:` is the separate
11
+ # field carrying the file. Both resolve relative to this Warble project's own root (the profile
12
+ # directory), not relative to this file.
13
+ #
14
+ # The host rewrites this `project:` line to the user's own wren project at bind time and regenerates
15
+ # `document:`'s file in place; the committed values pin this repo's own v5 example so the profile
16
+ # compiles standalone for CI/dev. See MAINTAINING.md for how to regenerate the committed document.
17
+ kind: prepared
9
18
  project: ../../../../examples/v5-jaffle
19
+ document: context/context.json
@@ -0,0 +1,146 @@
1
+ {
2
+ "context_version": 2,
3
+ "parseable": true,
4
+ "metrics": [
5
+ {
6
+ "name": "total_revenue",
7
+ "owner": "order_metrics",
8
+ "declared": true,
9
+ "additivity": "additive"
10
+ },
11
+ {
12
+ "name": "order_count",
13
+ "owner": "order_metrics",
14
+ "declared": true,
15
+ "additivity": "additive"
16
+ },
17
+ {
18
+ "name": "id",
19
+ "owner": "customers",
20
+ "declared": false,
21
+ "additivity": null
22
+ },
23
+ {
24
+ "name": "id",
25
+ "owner": "orders",
26
+ "declared": false,
27
+ "additivity": null
28
+ },
29
+ {
30
+ "name": "customer_id",
31
+ "owner": "orders",
32
+ "declared": false,
33
+ "additivity": null
34
+ },
35
+ {
36
+ "name": "amount",
37
+ "owner": "orders",
38
+ "declared": false,
39
+ "additivity": null
40
+ }
41
+ ],
42
+ "dimensions": [
43
+ {
44
+ "name": "customer_id",
45
+ "owner": "order_metrics",
46
+ "is_temporal": false
47
+ },
48
+ {
49
+ "name": "name",
50
+ "owner": "customers",
51
+ "is_temporal": false
52
+ },
53
+ {
54
+ "name": "order_date",
55
+ "owner": "orders",
56
+ "is_temporal": true
57
+ }
58
+ ],
59
+ "models": [
60
+ {
61
+ "name": "customers",
62
+ "has_timestamp": false,
63
+ "columns": [
64
+ "id",
65
+ "name"
66
+ ]
67
+ },
68
+ {
69
+ "name": "orders",
70
+ "has_timestamp": true,
71
+ "columns": [
72
+ "id",
73
+ "customer_id",
74
+ "order_date",
75
+ "amount"
76
+ ]
77
+ }
78
+ ],
79
+ "lineage": {
80
+ "nodes": [
81
+ {
82
+ "id": "model:customers",
83
+ "kind": "model"
84
+ },
85
+ {
86
+ "id": "model:orders",
87
+ "kind": "model"
88
+ },
89
+ {
90
+ "id": "rel:orders_customer",
91
+ "kind": "relationship"
92
+ },
93
+ {
94
+ "id": "cube:order_metrics",
95
+ "kind": "cube"
96
+ },
97
+ {
98
+ "id": "metric:order_metrics.total_revenue",
99
+ "kind": "metric"
100
+ },
101
+ {
102
+ "id": "metric:order_metrics.order_count",
103
+ "kind": "metric"
104
+ },
105
+ {
106
+ "id": "dim:order_metrics.customer_id",
107
+ "kind": "dimension"
108
+ },
109
+ {
110
+ "id": "query:total-revenue",
111
+ "kind": "query"
112
+ }
113
+ ],
114
+ "edges": [
115
+ {
116
+ "from": "model:orders",
117
+ "to": "rel:orders_customer"
118
+ },
119
+ {
120
+ "from": "model:customers",
121
+ "to": "rel:orders_customer"
122
+ },
123
+ {
124
+ "from": "model:orders",
125
+ "to": "cube:order_metrics"
126
+ },
127
+ {
128
+ "from": "cube:order_metrics",
129
+ "to": "metric:order_metrics.total_revenue"
130
+ },
131
+ {
132
+ "from": "cube:order_metrics",
133
+ "to": "metric:order_metrics.order_count"
134
+ },
135
+ {
136
+ "from": "cube:order_metrics",
137
+ "to": "dim:order_metrics.customer_id"
138
+ },
139
+ {
140
+ "from": "model:orders",
141
+ "to": "query:total-revenue"
142
+ }
143
+ ]
144
+ },
145
+ "lineage_diagnostics": []
146
+ }
@@ -4,6 +4,7 @@
4
4
  "borrowed_actions": [
5
5
  "introspect"
6
6
  ],
7
+ "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.",
7
8
  "context_binding": {
8
9
  "binding_mode": "runtime_selected",
9
10
  "project": "../../../../examples/v5-jaffle"
@@ -14,7 +15,7 @@
14
15
  }
15
16
  ],
16
17
  "context_requirements": [
17
- "a wren project (semantic layer) to introspect"
18
+ "a bound semantic context (semantic layer) to introspect"
18
19
  ],
19
20
  "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.",
20
21
  "effect": {
@@ -23,6 +24,7 @@
23
24
  },
24
25
  "render_blocks": []
25
26
  },
27
+ "entrypoint": true,
26
28
  "eval": {
27
29
  "metrics": [
28
30
  "coverage"
@@ -48,7 +50,7 @@
48
50
  "consumes": [],
49
51
  "name": "summarize_semantics",
50
52
  "produces": "semantic_summary",
51
- "prompt": "You introspect the `v5-jaffle` wren project (a semantic layer at `../../../../examples/v5-jaffle`) and return a\nstructured summary of what it contains — the map the other GenBI components build on.\n\n- Introspect the semantic layer with the `wren` CLI: run `wren context show` (and, if available,\n `wren cube list`) to read the models, columns, relationships, and metrics/cubes. This is the\n `raw_introspect_result` you consume — read 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.",
53
+ "prompt": "You introspect the bound semantic context `v5-jaffle` (a semantic layer at `../../../../examples/v5-jaffle`)\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.",
52
54
  "tier": "cheap",
53
55
  "when": null
54
56
  }
@@ -63,7 +65,7 @@
63
65
  ],
64
66
  "status": "pass"
65
67
  },
66
- "prompt_fragment": "## summarize_semantics\n\nYou introspect the `v5-jaffle` wren project (a semantic layer at `../../../../examples/v5-jaffle`) and return a\nstructured summary of what it contains — the map the other GenBI components build on.\n\n- Introspect the semantic layer with the `wren` CLI: run `wren context show` (and, if available,\n `wren cube list`) to read the models, columns, relationships, and metrics/cubes. This is the\n `raw_introspect_result` you consume — read 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.",
68
+ "prompt_fragment": "## summarize_semantics\n\nYou introspect the bound semantic context `v5-jaffle` (a semantic layer at `../../../../examples/v5-jaffle`)\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.",
67
69
  "realization_kind": "skill",
68
70
  "required_capabilities": [
69
71
  "semantic_introspection",
@@ -77,6 +79,7 @@
77
79
  },
78
80
  {
79
81
  "borrowed_actions": [],
82
+ "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.\nFor time-series questions, use the requested date range. If none is given, use the full\navailable data range and disclose that choice; never silently pick a recent N-week window.\nDistinguish calendar-relative dates from a window anchored on the latest data date. Preserve\nexplicit calendar-relative requests even if the data is old; do not silently re-anchor them.\nExplain exact date bounds, the date column, week start, timezone if known, and partial periods.\nUse semantic metric definitions where available. Otherwise state the chosen interpretation:\ndistinct ordering customers, first-order customers and cumulative customers are different.\nDo not present synonymous counts as different metrics. Do not invent registration dates or\nmetric meanings absent from context. Name the answer SQL in the terminal definition so that\nlater date-bound checks cannot replace the answering table.",
80
83
  "context_binding": {
81
84
  "binding_mode": "runtime_selected",
82
85
  "project": "../../../../examples/v5-jaffle"
@@ -87,31 +90,16 @@
87
90
  }
88
91
  ],
89
92
  "context_requirements": [
90
- "a wren project (semantic layer) to query"
93
+ "a bound semantic context (semantic layer) to query"
91
94
  ],
92
95
  "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.",
93
96
  "effect": {
94
97
  "outcome": {
95
98
  "kind": "none"
96
99
  },
97
- "render_blocks": [
98
- {
99
- "fields": {
100
- "columns": "string[]",
101
- "rows": "row[]"
102
- },
103
- "type": "table"
104
- },
105
- {
106
- "fields": {
107
- "filters": "string[]",
108
- "source_tables": "string[]",
109
- "sql": "string"
110
- },
111
- "type": "definition"
112
- }
113
- ]
100
+ "render_blocks": []
114
101
  },
102
+ "entrypoint": true,
115
103
  "eval": {
116
104
  "metrics": [
117
105
  "execution_accuracy",
@@ -148,11 +136,14 @@
148
136
  "id": "answer_query",
149
137
  "llm_calls": [
150
138
  {
139
+ "capabilities": [
140
+ "llm:cheap"
141
+ ],
151
142
  "conditional": false,
152
143
  "consumes": [],
153
144
  "name": "resolve_intent",
154
145
  "produces": "query_intent",
155
- "prompt": "You answer a single data question over the `v5-jaffle` wren project (a semantic layer at\n`../../../../examples/v5-jaffle`). This first step resolves the user's question into a concrete query intent.\n\n- If unsure of the schema, introspect first: `wren context show`.\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.",
146
+ "prompt": "You answer a single data question over the bound semantic context `v5-jaffle` (a semantic\nlayer at `../../../../examples/v5-jaffle`). This first step resolves the user's question into a concrete query intent.\n\n- Do not call any tool in this step: it declares no data capability. Resolve the question from the\n semantic context you were given; the next step writes and runs the query.\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.",
156
147
  "tier": "cheap",
157
148
  "when": null
158
149
  },
@@ -163,7 +154,7 @@
163
154
  ],
164
155
  "name": "generate_sql",
165
156
  "produces": "query_result",
166
- "prompt": "Given the resolved `query_intent`, write and execute the query through the semantic layer.\n\n- Query ONLY through the wren CLI, which returns JSON: `wren -q -o json -s '<SQL>'`. 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 rows from `wren` 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.",
157
+ "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\": {\"query_id\": \"<the query_id the tool result returned>\"}}\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- When the tool result carries a `query_id`, never copy SQL, filters or source tables into\n `definition`: cite `{\"query_id\": …}` only. When it carries none, cite\n `{\"sql\": \"<the exact SQL you ran>\", \"source_tables\": [\"...\"]}` and nothing else. No shape of\n `definition` has a `filters` key.\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- Your final message is `query_result` as JSON and nothing else: no heading, no sentence before or\n after it, no Markdown fence, no reasoning tags. Every string closed and every bracket matched; it\n must parse as JSON.",
167
158
  "tier": "strong",
168
159
  "when": null
169
160
  },
@@ -174,7 +165,7 @@
174
165
  ],
175
166
  "name": "repair_sql",
176
167
  "produces": "repaired_result",
177
- "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 `wren -q -o json -s '<SQL>'`. 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 rows from `wren` 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).",
168
+ "prompt": "Repair step — runs ONLY in one of these cases:\n\n- When `query_result` is `{\"status\": \"error\", \"code\": \"step_failed\", \"reason\":\n \"terminal_unparseable: …\", \"text\": …}`, the previous answer is in `text`; when `query_result`\n is otherwise not valid JSON (prose around it, an unterminated string, a stray reasoning tag),\n the previous answer is that raw text. Re-emit that SAME answer as one JSON object in the canonical\n shape below, citing its query by its `query_id` (or, when its tool result carried none,\n `{\"sql\": \"<the exact SQL you ran>\", \"source_tables\": [\"...\"]}`).\n Run no new query, and put nothing else in the message.\n- `query_result` came back with an execution error or an empty/obviously wrong result.\n\nIf the previous step succeeded with a valid JSON, sensible result, do nothing and pass it through.\n\n- For an execution failure (the second case above): 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\": {\"query_id\": \"<the query_id the tool result returned>\"}}\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).\n When the tool result carries a `query_id`, never copy SQL, filters or source tables into\n `definition`: cite `{\"query_id\": …}` only. When it carries none, cite\n `{\"sql\": \"<the exact SQL you ran>\", \"source_tables\": [\"...\"]}` and nothing else. No shape of\n `definition` has a `filters` key.\n Your final message is the JSON object (or the refusal object) and nothing else: no heading, no\n sentence before or after it, no Markdown fence, no reasoning tags. Every string closed and every\n bracket matched; it must parse as JSON.",
178
169
  "tier": "strong",
179
170
  "when": {
180
171
  "guard": "on_failure",
@@ -192,7 +183,7 @@
192
183
  ],
193
184
  "status": "pass"
194
185
  },
195
- "prompt_fragment": "## resolve_intent\n\nYou answer a single data question over the `v5-jaffle` wren project (a semantic layer at\n`../../../../examples/v5-jaffle`). This first step resolves the user's question into a concrete query intent.\n\n- If unsure of the schema, introspect first: `wren context show`.\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 wren CLI, which returns JSON: `wren -q -o json -s '<SQL>'`. 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 rows from `wren` 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 `wren -q -o json -s '<SQL>'`. 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 rows from `wren` 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).",
186
+ "prompt_fragment": "## resolve_intent\n\nYou answer a single data question over the bound semantic context `v5-jaffle` (a semantic\nlayer at `../../../../examples/v5-jaffle`). This first step resolves the user's question into a concrete query intent.\n\n- Do not call any tool in this step: it declares no data capability. Resolve the question from the\n semantic context you were given; the next step writes and runs the query.\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\": {\"query_id\": \"<the query_id the tool result returned>\"}}\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- When the tool result carries a `query_id`, never copy SQL, filters or source tables into\n `definition`: cite `{\"query_id\": …}` only. When it carries none, cite\n `{\"sql\": \"<the exact SQL you ran>\", \"source_tables\": [\"...\"]}` and nothing else. No shape of\n `definition` has a `filters` key.\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- Your final message is `query_result` as JSON and nothing else: no heading, no sentence before or\n after it, no Markdown fence, no reasoning tags. Every string closed and every bracket matched; it\n must parse as JSON.\n\n## repair_sql\n\nRepair step — runs ONLY in one of these cases:\n\n- When `query_result` is `{\"status\": \"error\", \"code\": \"step_failed\", \"reason\":\n \"terminal_unparseable: …\", \"text\": …}`, the previous answer is in `text`; when `query_result`\n is otherwise not valid JSON (prose around it, an unterminated string, a stray reasoning tag),\n the previous answer is that raw text. Re-emit that SAME answer as one JSON object in the canonical\n shape below, citing its query by its `query_id` (or, when its tool result carried none,\n `{\"sql\": \"<the exact SQL you ran>\", \"source_tables\": [\"...\"]}`).\n Run no new query, and put nothing else in the message.\n- `query_result` came back with an execution error or an empty/obviously wrong result.\n\nIf the previous step succeeded with a valid JSON, sensible result, do nothing and pass it through.\n\n- For an execution failure (the second case above): 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\": {\"query_id\": \"<the query_id the tool result returned>\"}}\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).\n When the tool result carries a `query_id`, never copy SQL, filters or source tables into\n `definition`: cite `{\"query_id\": …}` only. When it carries none, cite\n `{\"sql\": \"<the exact SQL you ran>\", \"source_tables\": [\"...\"]}` and nothing else. No shape of\n `definition` has a `filters` key.\n Your final message is the JSON object (or the refusal object) and nothing else: no heading, no\n sentence before or after it, no Markdown fence, no reasoning tags. Every string closed and every\n bracket matched; it must parse as JSON.",
196
187
  "realization_kind": "skill",
197
188
  "required_capabilities": [
198
189
  "sql_execution:read_only",
@@ -211,15 +202,16 @@
211
202
  "topic_default": "overview"
212
203
  },
213
204
  "borrowed_actions": [],
205
+ "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.",
214
206
  "context_binding": {
215
207
  "binding_mode": "runtime_selected",
216
208
  "project": "../../../../examples/v5-jaffle"
217
209
  },
218
210
  "context_precondition": [],
219
211
  "context_requirements": [
220
- "a wren project (semantic layer) to build dashboards over"
212
+ "a bound semantic context (semantic layer) to build dashboards over"
221
213
  ],
222
- "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.",
214
+ "description": "Build a multi-panel dashboard on a topic: plan which panel questions answer it, obtain a verified result for each panel through the profile's bound answer behavior, 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.",
223
215
  "effect": {
224
216
  "outcome": {
225
217
  "kind": "none"
@@ -252,7 +244,7 @@
252
244
  },
253
245
  {
254
246
  "fields": {
255
- "filters": "string[]",
247
+ "filters": "string[]?",
256
248
  "source_tables": "string[]",
257
249
  "sql": "string"
258
250
  },
@@ -260,6 +252,7 @@
260
252
  }
261
253
  ]
262
254
  },
255
+ "entrypoint": true,
263
256
  "eval": {
264
257
  "metrics": [
265
258
  "panel_validity",
@@ -291,18 +284,24 @@
291
284
  "consumes": [],
292
285
  "name": "plan_dashboard",
293
286
  "produces": "dashboard_plan",
294
- "prompt": "You build data dashboards over the `v5-jaffle` wren project (a semantic layer at\n`../../../../examples/v5-jaffle`).\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 `wren` CLI\n (`wren context show`, `wren cube list`, `wren cube describe <cube>`). Do not assume the schema —\n 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 `wren`; never\n hand-write SQL against raw tables outside the model.",
287
+ "prompt": "You build data dashboards over the bound semantic context `v5-jaffle` (a semantic layer at\n`../../../../examples/v5-jaffle`).\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- Decide which business questions 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, give its title, panel type (kpi_card | table | chart),\n and one self-contained natural-language data question whose verified answer will populate it.\n Do not inspect the schema, write a query, or invent data in this step; the bound answer behavior\n resolves each panel question against its own semantic context and query authority.",
295
288
  "tier": "strong",
296
289
  "when": null
297
290
  },
298
291
  {
292
+ "component_calls": [
293
+ {
294
+ "alias": "answer",
295
+ "component": "answer_query"
296
+ }
297
+ ],
299
298
  "conditional": false,
300
299
  "consumes": [
301
300
  "dashboard_plan"
302
301
  ],
303
302
  "name": "compose_layout",
304
303
  "produces": "dashboard",
305
- "prompt": "Given the `dashboard_plan`, run each panel's query and compose the dashboard.\n\n- Run each planned panel query with the `wren` CLI (`wren -q -o json -s '<SQL>'`) and collect the\n results — these are the `panel_results` you consume. (This inlines the query behavior; it does not\n call a 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).",
304
+ "prompt": "Given the `dashboard_plan`, obtain each panel's verified answer and compose the dashboard.\n\n- For every planned panel, call the logical `answer` alias once with a concise request to answer\n that panel's natural-language data question. Include the panel title and type in the optional\n structured input. Call the same alias again for every additional panel; do not combine unrelated\n panels into one request merely to avoid repeated calls.\n- Accept a panel result only when the call returns `status: \"ok\"`, `output.kind: \"value\"`, and a\n value containing `verified: true`, `columns`, `rows`, `summary`, and `definition`. Refuse the\n dashboard rather than using a refused, failed, unverified, malformed, or placeholder result.\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. Panels reach\n this step with SQL provenance; a host that issues `query_id`s resolves them first. Derive the\n dashboard's definition block from the accepted panel definitions: copy `sql` and `source_tables`\n when the panel carries them, include `filters` only if the panel carries them, and never invent\n provenance.\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 the accepted 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).",
306
305
  "tier": "cheap",
307
306
  "when": null
308
307
  }
@@ -318,16 +317,15 @@
318
317
  "checks": [],
319
318
  "status": "pass"
320
319
  },
321
- "prompt_fragment": "## plan_dashboard\n\nYou build data dashboards over the `v5-jaffle` wren project (a semantic layer at\n`../../../../examples/v5-jaffle`).\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 `wren` CLI\n (`wren context show`, `wren cube list`, `wren cube describe <cube>`). Do not assume the schema —\n 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 `wren`; never\n 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 with the `wren` CLI (`wren -q -o json -s '<SQL>'`) and collect the\n results — these are the `panel_results` you consume. (This inlines the query behavior; it does not\n call a 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).",
320
+ "prompt_fragment": "## plan_dashboard\n\nYou build data dashboards over the bound semantic context `v5-jaffle` (a semantic layer at\n`../../../../examples/v5-jaffle`).\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- Decide which business questions 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, give its title, panel type (kpi_card | table | chart),\n and one self-contained natural-language data question whose verified answer will populate it.\n Do not inspect the schema, write a query, or invent data in this step; the bound answer behavior\n resolves each panel question against its own semantic context and query authority.\n\n## compose_layout\n\nGiven the `dashboard_plan`, obtain each panel's verified answer and compose the dashboard.\n\n- For every planned panel, call the logical `answer` alias once with a concise request to answer\n that panel's natural-language data question. Include the panel title and type in the optional\n structured input. Call the same alias again for every additional panel; do not combine unrelated\n panels into one request merely to avoid repeated calls.\n- Accept a panel result only when the call returns `status: \"ok\"`, `output.kind: \"value\"`, and a\n value containing `verified: true`, `columns`, `rows`, `summary`, and `definition`. Refuse the\n dashboard rather than using a refused, failed, unverified, malformed, or placeholder result.\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. Panels reach\n this step with SQL provenance; a host that issues `query_id`s resolves them first. Derive the\n dashboard's definition block from the accepted panel definitions: copy `sql` and `source_tables`\n when the panel carries them, include `filters` only if the panel carries them, and never invent\n provenance.\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 the accepted 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).",
322
321
  "realization_kind": "skill",
323
322
  "required_capabilities": [
324
- "sql_execution:read_only",
325
- "genbi_build",
326
323
  "render_contract",
327
324
  "artifact_write",
328
325
  "llm:per_step_tier",
329
326
  "llm:strong",
330
- "llm:cheap"
327
+ "llm:cheap",
328
+ "component_invocation"
331
329
  ],
332
330
  "trigger": {
333
331
  "kind": "one_shot"
@@ -337,13 +335,14 @@
337
335
  },
338
336
  {
339
337
  "borrowed_actions": [],
338
+ "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>'`.",
340
339
  "context_binding": {
341
340
  "binding_mode": "runtime_selected",
342
341
  "project": "../../../../examples/v5-jaffle"
343
342
  },
344
343
  "context_precondition": [],
345
344
  "context_requirements": [
346
- "a wren project with an additive metric plus a time dimension and a groupable dimension"
345
+ "a bound semantic context with an additive metric plus a time dimension and a groupable dimension"
347
346
  ],
348
347
  "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.",
349
348
  "effect": {
@@ -360,6 +359,7 @@
360
359
  }
361
360
  ]
362
361
  },
362
+ "entrypoint": true,
363
363
  "eval": {
364
364
  "metrics": [
365
365
  "driver_correctness",
@@ -400,7 +400,7 @@
400
400
  "consumes": [],
401
401
  "name": "plan_decomposition",
402
402
  "produces": "decomposition_plan",
403
- "prompt": "You explain why a metric changed over the `v5-jaffle` wren project (a semantic layer at\n`../../../../examples/v5-jaffle`), by planning how to decompose the change.\n\n- Introspect the layer as needed (`wren context show`) to find the metric, its time dimension, and\n 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).",
403
+ "prompt": "You explain why a metric changed over the bound semantic context `v5-jaffle` (a semantic\nlayer at `../../../../examples/v5-jaffle`), 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).",
404
404
  "tier": "strong",
405
405
  "when": null
406
406
  },
@@ -411,7 +411,7 @@
411
411
  ],
412
412
  "name": "synthesize_drivers",
413
413
  "produces": "driver_explanation",
414
- "prompt": "Given the `decomposition_plan`, quantify the change and synthesize the drivers into an explanation.\n\n- Run the decomposition queries through the `wren` CLI (`wren -q -o json -s '<SQL>'`): compute the\n metric for each period and the per-dimension contribution to the delta. Rank contributors by the\n size of their 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.",
414
+ "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.",
415
415
  "tier": "strong",
416
416
  "when": null
417
417
  }
@@ -421,7 +421,7 @@
421
421
  "checks": [],
422
422
  "status": "pass"
423
423
  },
424
- "prompt_fragment": "## plan_decomposition\n\nYou explain why a metric changed over the `v5-jaffle` wren project (a semantic layer at\n`../../../../examples/v5-jaffle`), by planning how to decompose the change.\n\n- Introspect the layer as needed (`wren context show`) to find the metric, its time dimension, and\n 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 `wren` CLI (`wren -q -o json -s '<SQL>'`): compute the\n metric for each period and the per-dimension contribution to the delta. Rank contributors by the\n size of their 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.",
424
+ "prompt_fragment": "## plan_decomposition\n\nYou explain why a metric changed over the bound semantic context `v5-jaffle` (a semantic\nlayer at `../../../../examples/v5-jaffle`), 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.",
425
425
  "realization_kind": "skill",
426
426
  "required_capabilities": [
427
427
  "sql_execution:read_only",
@@ -456,10 +456,6 @@
456
456
  }
457
457
  ],
458
458
  "lineage": {
459
- "consumers": {
460
- "dashboards": 0,
461
- "queries": 1
462
- },
463
459
  "edges": 7,
464
460
  "nodes": 8,
465
461
  "resolvable": true
@@ -502,5 +498,5 @@
502
498
  }
503
499
  },
504
500
  "profile": "genbi-default",
505
- "warble_ir_version": "0.6"
501
+ "warble_ir_version": "0.9"
506
502
  }
@@ -10,7 +10,38 @@ context:
10
10
  project: ./context/binding.yml
11
11
 
12
12
  components:
13
+ # Each mount's `brief` carries this binding's concrete data-access framing — the wren CLI
14
+ # invocations the underlying Hub component prompts used to name directly before they were
15
+ # neutralized. It replaces the component's own `brief` wholesale and is assembled into every
16
+ # step's system prompt for that component (see Warble's docs/spec/authoring.md, "brief").
13
17
  - use: explore_model
18
+ brief: |
19
+ Data access in this deployment goes through the `wren` CLI. Introspect the semantic layer
20
+ with `wren context show` (and, where available, `wren cube list`) to read models, columns,
21
+ relationships, and metrics/cubes — do not assume the schema.
14
22
  - use: answer_query
23
+ brief: |
24
+ Data access in this deployment goes through the `wren` CLI. If you need to introspect the
25
+ schema, run `wren context show`. Query the semantic layer with `wren -q -o json -s '<SQL>'`,
26
+ which returns JSON; object-shaped rows are also valid — preserve their values exactly. Never
27
+ hand-write SQL against raw tables outside the model.
28
+ For time-series questions, use the requested date range. If none is given, use the full
29
+ available data range and disclose that choice; never silently pick a recent N-week window.
30
+ Distinguish calendar-relative dates from a window anchored on the latest data date. Preserve
31
+ explicit calendar-relative requests even if the data is old; do not silently re-anchor them.
32
+ Explain exact date bounds, the date column, week start, timezone if known, and partial periods.
33
+ Use semantic metric definitions where available. Otherwise state the chosen interpretation:
34
+ distinct ordering customers, first-order customers and cumulative customers are different.
35
+ Do not present synonymous counts as different metrics. Do not invent registration dates or
36
+ metric meanings absent from context. Name the answer SQL in the terminal definition so that
37
+ later date-bound checks cannot replace the answering table.
15
38
  - use: generate_dashboard
39
+ brief: |
40
+ Data access in this deployment goes through the `wren` CLI. Discover the schema at query
41
+ time with `wren context show`, `wren cube list`, and `wren cube describe <cube>` — do not
42
+ assume it. Run each panel query with `wren -q -o json -s '<SQL>'`; every query goes through
43
+ `wren`, never hand-written SQL against raw tables outside the model.
16
44
  - use: explain_change
45
+ brief: |
46
+ Data access in this deployment goes through the `wren` CLI. Introspect the layer as needed
47
+ with `wren context show`. Run the decomposition queries with `wren -q -o json -s '<SQL>'`.
@@ -1,4 +1,15 @@
1
- # A pinned, existing semantic project. Runtime hosts replace this fixture binding with the project
2
- # selected at bind time and must present a successful context-build proof before starting a run.
3
- # Path is relative to this profile directory.
1
+ # A pinned, existing semantic project, bound as a PREPARED document: `kind: prepared` means Warble
2
+ # reads the JSON document `document:` names — produced by WrenAI's own `wren-context-loader`
3
+ # generator — rather than introspecting the project's MDL itself.
4
+ #
5
+ # `project:` is the layer's IDENTITY, never the document's path; `document:` carries the file. Both
6
+ # resolve relative to this Warble project's own root (the profile directory), not relative to this
7
+ # file.
8
+ #
9
+ # Runtime hosts replace this fixture binding with the project selected at bind time (rewriting
10
+ # `project:` and regenerating `document:`'s file in place) and must present a successful
11
+ # context-build proof before starting a run. See MAINTAINING.md for how to regenerate the committed
12
+ # document.
13
+ kind: prepared
4
14
  project: ../../../../examples/v5-jaffle
15
+ document: context/context.json