SKILL.md
Adding a Query Mode
Drizzle-cube has four analysis modes: query, funnel, flow, and retention. Each mode requires coordinated changes across backend (types → query builder → executor → compiler → cache), frontend (types → adapter → store slice → hooks → UI), tests, and an icon. Follow this checklist in order.
Documentation Context
Before designing or updating an analysis/query mode, review https://www.drizzle-cube.dev/llms.txt for the current documentation map, public API guidance, and mode-related examples. Use repository source and the checklist below as the implementation source of truth when docs and code differ.
Checklist
Backend
- 1. Create type definitions — Create
src/server/types/{mode}.tswith{Mode}QueryConfig,{Mode}ResultRow, and{Mode}ValidationResultinterfaces. Export fromsrc/server/types/index.ts. Add optional{mode}?: {Mode}QueryConfigfield toSemanticQueryinsrc/server/types/query.ts.
- 2. Create query builder — Create
src/server/builders/{mode}-query-builder.tswith:has{Mode}(query)detection,validateConfig(config, cubes)validation,build{Mode}Query(config, cubes, context)SQL generation via Drizzle, andtransformResult(rawResult)post-processing.
- 3. Integrate with executor — In
src/server/executor.ts, add a{mode}QueryBuilderfield to theQueryExecutorclass, initialize it in the constructor, add detection in theexecute()method, add routing to a newexecute{Mode}QueryWithCache()method, and implementexecute{Mode}Query()anddryRun{Mode}()methods.
- 4. Integrate with compiler — In
src/server/compiler.ts, add adryRun{Mode}()public method, add a validation path invalidateQueryAgainstCubes(), and add routing inexplainQuery().
- 5. Update cache utils — In
src/server/cache-utils.ts, addnormalize{Mode}Config()and wire it intonormalizeQuery().
- 6. Database adapter updates (if mode needs DB-specific SQL) — Add abstract methods to
src/server/adapters/base-adapter.tsand implement in postgres, mysql, sqlite, duckdb, snowflake, singlestore, and databend adapters.
- 7. Update AI prompts (if AI should understand this mode) — Update
src/server/prompts/types.tsandsrc/server/prompts/step1-shape-prompt.tswith mode-specific query structures.
Frontend
- 8. Create frontend types — Create
src/client/types/{mode}.tswith{Mode}SliceState,Server{Mode}Query,{Mode}ChartData, and type guards. Export public types fromsrc/client/index.ts.
- 9. Update AnalysisConfig types — In
src/client/types/analysisConfig.ts, add'{mode}'to theAnalysisTypeunion, create{Mode}AnalysisConfigextendingAnalysisConfigBase, and add to theAnalysisConfigunion type.
- 10. Create mode adapter — Create
src/client/adapters/{mode}ModeAdapter.tsimplementingModeAdapter<{Mode}SliceState>with:type,createInitial(),extractState()(critical for workspace persistence),canLoad(),load(),save(),validate(),clear(),getDefaultChartConfig(). Register viaadapterRegistry.register()insrc/client/adapters/adapterRegistry.ts.
- 11. Create store slice — Create
src/client/stores/slices/{mode}Slice.tswith state interface, actions (includingbuild{Mode}Query()returningServer{Mode}Query | nullandis{Mode}ModeEnabled()), andcreateInitial{Mode}State(). Export fromsrc/client/stores/slices/index.ts. Compose intosrc/client/stores/analysisBuilderStore.tsx.
- 12. Create query hook — Create
src/client/hooks/queries/use{Mode}Query.tshandling debouncing, caching, and result transformation viauseQuery. Export fromsrc/client/hooks/queries/index.ts. A single shareduseDryRunQuery.tsalready handles SQL preview for all modes.
- 13. Update query execution — In
src/client/hooks/useAnalysisQueryExecution.ts, addserver{Mode}Queryto the options interface and add mode routing. Insrc/client/hooks/useAnalysisBuilderHook.ts, add mode-specific state and actions to the hook result.
- 14. Create UI components — Create
src/client/components/AnalysisBuilder/{Mode}ModeContent.tsx(main mode panel) and{Mode}ConfigPanel.tsx(configuration UI). UpdateAnalysisQueryPanel.tsxto conditionally render{Mode}ModeContent. UpdateAnalysisTypeSelector.tsxto add the mode option.
- 15. Update portlet integration — Update
src/client/components/AnalyticsPortlet.tsxandsrc/client/components/PortletContainer.tsxto detect and handle the new mode's queries.
- 16. Add icon — In
src/client/icons/customIcons.ts, create{mode}Icon: IconifyIconwith appropriate SVG path data.
- 17. Chart type integration (if mode needs a new chart type) — Create chart component in
src/client/components/charts/, updatesrc/client/shared/chartDefaults.tsgetChartAvailability(), and updateChartTypeSelectorexcludeTypes.
Testing
- 18. Backend tests — Create
tests/{mode}-query.test.tscovering query builder methods, execution integration, security context isolation, cross-cube filtering, and multi-database compatibility (TESTDBTYPEenv).
- 19. Client adapter tests — Create
tests/client/adapters/{mode}ModeAdapter.test.tscoveringcreateInitial(),canLoad(),load(),save(),validate(),clear(),getDefaultChartConfig(), and a round-trip test (save → loadstate equivalence).
- 20. Client validation and execution tests — Create
tests/client/{mode}/{mode}Validation.test.tsandtests/client/{mode}/{mode}Execution.test.tsfor validation functions, data transformations, and metric calculations.
File Reference
| File | Action | Key Symbols |
|---|---|---|
src/server/types/{mode}.ts |
Create | {Mode}QueryConfig, {Mode}ResultRow |
src/server/types/index.ts |
Export new types | — |
src/server/types/query.ts |
Add field to union | SemanticQuery |
src/server/builders/{mode}-query-builder.ts |
Create | has{Mode}(), build{Mode}Query(), validateConfig(), transformResult() |
src/server/executor.ts |
Add builder + routing | QueryExecutor, execute(), execute{Mode}QueryWithCache() |
src/server/compiler.ts |
Add public methods | dryRun{Mode}(), validateQueryAgainstCubes(), explainQuery() |
src/server/cache-utils.ts |
Add normalizer | normalize{Mode}Config(), normalizeQuery() |
src/client/types/{mode}.ts |
Create | {Mode}SliceState, Server{Mode}Query, {Mode}ChartData |
src/client/types/analysisConfig.ts |
Extend union + config | AnalysisType, {Mode}AnalysisConfig |
src/client/adapters/{mode}ModeAdapter.ts |
Create | {mode}ModeAdapter : ModeAdapter |
src/client/adapters/adapterRegistry.ts |
Register adapter | adapterRegistry.register() |
src/client/stores/slices/{mode}Slice.ts |
Create | create{Mode}Slice, createInitial{Mode}State() |
src/client/stores/analysisBuilderStore.tsx |
Compose slice | createAnalysisBuilderStore() |
src/client/hooks/queries/use{Mode}Query.ts |
Create | use{Mode}Query() |
src/client/hooks/useAnalysisQueryExecution.ts |
Add mode routing | server{Mode}Query |
src/client/hooks/useAnalysisBuilderHook.ts |
Add state/actions | — |
src/client/components/AnalysisBuilder/{Mode}ModeContent.tsx |
Create | — |
src/client/components/AnalysisBuilder/{Mode}ConfigPanel.tsx |
Create | — |
src/client/components/AnalysisBuilder/AnalysisTypeSelector.tsx |
Add option | — |
src/client/components/AnalysisBuilder/AnalysisQueryPanel.tsx |
Add conditional render | — |
src/client/components/AnalyticsPortlet.tsx |
Add mode detection | — |
src/client/icons/customIcons.ts |
Add icon | {mode}Icon : IconifyIcon |
Reference Implementations
Study these existing modes as patterns (paths only — read the files directly):
Funnel — src/server/builders/funnel-query-builder.ts, src/server/types/funnel.ts, src/client/adapters/funnelModeAdapter.ts, src/client/types/funnel.ts, src/client/stores/slices/funnelSlice.ts, src/client/hooks/queries/useFunnelQuery.ts, src/client/components/AnalysisBuilder/FunnelModeContent.tsx, src/client/components/AnalysisBuilder/FunnelConfigPanel.tsx, tests/funnel-query.test.ts, tests/client/adapters/funnelModeAdapter.test.ts
Flow — src/server/builders/flow-query-builder.ts, src/server/types/flow.ts, src/client/adapters/flowModeAdapter.ts, src/client/types/flow.ts, src/client/stores/slices/flowSlice.ts, src/client/hooks/queries/useFlowQuery.ts, src/client/components/AnalysisBuilder/FlowModeContent.tsx, tests/flow-query.test.ts, tests/client/adapters/flowModeAdapter.test.ts
Retention — src/server/builders/retention-query-builder.ts, src/server/types/retention.ts, src/client/adapters/retentionModeAdapter.ts, src/client/types/retention.ts, src/client/stores/slices/retentionSlice.ts, src/client/hooks/queries/useRetentionQuery.ts, src/client/components/AnalysisBuilder/RetentionModeContent.tsx, src/client/components/AnalysisBuilder/RetentionConfigPanel.tsx, tests/retention-query.test.ts, tests/client/adapters/retentionModeAdapter.test.ts
Verification
npx tsc --noEmit— zero type errorsnpm test -- {mode}-query.test.ts— backend tests passnpm test -- {mode}ModeAdapter.test.ts— adapter tests passnpm run build— successful build- New mode appears in AnalysisTypeSelector with correct icon
- Mode adapter round-trips correctly (save → load preserves state)