Creating and Maintaining Cubes in the Thinkwise Software Factory
A cube is an analytical dataset built on top of one existing subject (table or view), for interactive pivoting, slicing, and charting — not a copy of the subject's data, and not a replacement for the subject's own Grid/Form. It classifies the subject's fields into two kinds:
- Dimensions — categorical fields used to segment and group: time, customer, product, region, status, work center.
- Values — measurable facts aggregated within that grouping: revenue, quantity, hours, a record
count. (The schema's own enum literal for this is
measure; every UI, doc, and this skill call it a Value — see "Naming traps" below.)
A cube view is one saved, named arrangement of those fields — which dimensions sit in which axis, which values are shown, filters, sorting, totals, and pivot/chart presentation. One cube commonly carries several views, each answering one specific question.
Subject at a defined grain (one row = one clearly stated business fact)
├─ Dimensions: who / what / where / when / category
└─ Values: amount / quantity / duration / count
↓
Cube view
├─ Filters (restrict without becoming an axis)
├─ Categories/Rows (primary drill path)
├─ Series/Columns (secondary axis, low cardinality)
└─ Values (the aggregated numbers shown)
↓
Pivot table and/or chart
Apply this whenever an MCP connector with Software Factory access creates, inspects, or modifies a
cube, cube field, or cube view — follow the connector's standard discovery→act flow; never guess
entity/task/property names. Everything below was confirmed live against a connected model (domain
key sf/manage_cubes for the cube family itself; sf/manage_datamodel for the per-variant overrides
and screen-type wiring) — try it directly first, and only escalate to
search_capabilities/get_available_domains on a rejection rather than re-discovering a domain
that already resolved earlier this session. This skill is connector-agnostic: it names entities,
tasks, and fields, not any one connector's literal tool names.
Companion skills, not duplicated here: general screen-type/component mechanics (screen_type,
tab, main_screen_type_id, how components attach to a screen) are covered in
thinkwise_software_factory_build_planner's interaction-surface step and the screen-type API-quirk
note in thinkwise_datamodeling_guidelines — a new screen type can't be created through the MCP
tools, so a cube's screen types (cube, cube_horizontal, cube_no_fields are the default
cube-capable ones) and components (Pivot table, Chart, Cube panel, Cube view bar) must be assigned to
a table by picking from what already exists. Table-variant inheritance mechanics in general (field-level vs.
snapshot, tab_variant_change, tab_variant_used) live in thinkwise_software_factory_variants —
this skill covers only the cube-specific override entities (tab_variant_cube_overview,
tab_variant_cube_view_overview). The general conditional-layout mechanic (colors, fonts, condition
types) lives in thinkwise_software_factory_conditional_layouts, but note
cube_view_field_conditional_layout is a separate, cube-specific entity family, not a row in
that skill's conditional_layout table — see "Conditional layouts on a cube view" below.
Golden rule — this is a judgment-call skill, not just a mechanics reference
Building a cube field or view is a handful of API calls. The part that actually matters — and the part every piece of Thinkwise's own documentation and community feedback stresses — is getting the analytical design right before touching the model:
- State the grain first. What does one row of the source subject mean? ("One invoiced sales line," "one stock position per item and location.") If a join behind the subject can duplicate a business fact — history joins, many-to-many, mixing transaction rows with pre-aggregated totals — the cube's totals will be wrong even though every individual aggregation is technically correct. Validate the source subject independently before creating the cube.
- Classify every field deliberately. The generated proposal from
task_enrichment_create_cubeis a starting point, not a finished cube — the documentation explicitly calls out reviewing and removing semantic-free ID columns, confirming values come from the most detailed (fact-grain) data source, and re-checking every auto-assigned dimension/value split. - Classify each value's additivity. Additive (sums cleanly across every dimension, e.g. sales quantity), semi-additive (sums across some dimensions but not time — e.g. an end-of-day balance), or non-additive (percentages, ratios, unit prices, distinct counts). A cube that silently lets a user sum a snapshot balance across months is easy to build and wrong to read.
- Decide if a cube is even the right tool. See the comparison table below — a cube is for repeated, rearrangeable, drill-and-pivot analysis; it is not a substitute for a Grid/Form (record inspection/editing), a report (pixel-perfect fixed documents), a couple of fixed KPI tiles, or an enterprise BI platform (Power BI/Qlik/Tableau) for cross-system governed analytics — Thinkwise documentation explicitly recommends OData export to those tools for that scale.
- Ask before guessing on: the cube's/view's/field's name when the model's naming isn't obvious,
whether a value should be editable (see "Editable values" below — this has hard technical
preconditions, not just a checkbox), each value's additivity classification
(additive/semi-additive/non-additive) whenever it isn't obvious from the business meaning, any
dimension-vs-value split that diverges from what
task_enrichment_create_cubeauto-generated, and whether a described "one more axis" request is actually a sign the view has become an unreadable everything-view (avoid one cube view with dozens of dimensions/values — prefer several focused views).
| Need | Prefer |
|---|---|
| Inspect or edit individual records | Grid/Form |
| A few fixed KPIs | Dashboard, tiles, or a purpose-built view |
| A pixel-perfect official document | Report |
| Cross-system, governed enterprise analytics at scale | Power BI/Qlik/Tableau via OData export |
| A fixed simple trend, no pivoting needed | A plain chart on a focused subject |
| Users repeatedly rearrange/drill/slice operational data | Cube |
Object graph — confirmed live
cube (one per source subject/table, domain key sf/manage_cubes)
├─ cube_field (one per dimension/value; cube_field_type: dimension | measure)
│ └─ cube_field_query (per-RDBMS SQL expression, for calculated/formula fields)
├─ cube_view (one saved pivot/chart configuration)
│ ├─ cube_view_field (places one cube_field into an area of this view)
│ │ ├─ cube_view_field_filter (default filter values, when area = filter)
│ │ ├─ cube_view_field_total (extra total rows/cols, when area = value)
│ │ └─ cube_view_field_conditional_layout (cube-specific conditional formatting)
│ ├─ cube_view_constant_line (target/threshold lines on a chart axis)
│ └─ cube_view_field_set_up (mirror entity backing the visual "Cube set-up panel")
├─ cube_view_grp (groups views into a submenu of the cube view bar)
├─ chart_legend_color (custom per-member series colors on a value's chart legend)
├─ role_cube_overview / role_cube_field_overview (per-role rights, incl. cube-panel & edit rights)
└─ tab_variant_cube_overview / tab_variant_cube_view_overview (per-table-variant overrides,
domain key sf/manage_datamodel — see "Per-variant overrides" below)
Every one of these keys off model_id, branch_id, cube_id first, then its own id chain — the same
pattern as every other Software Factory object family.
Creating a cube
task_enrichment_create_cube — bound to the cube entity itself, mandatory tab_id (the source
table), optional cube_description. Documented/expected to be addressable directly by the new
(model_id, branch_id, cube_id) you want to create — the same "the task creates its own bound row"
pattern used by task_create_tab_variant elsewhere in the model. Verified live this did not
work: staging this task against a not-yet-existing cube's key was consistently rejected (across
repeated attempts and domain variants) rather than creating the row. The reliable fallback, confirmed
live: skip the task entirely and create cube with a plain add — set model_id/branch_id/
cube_id/cube_description/allow_dragging_fields by hand, the same as any other top-level entity.
This forgoes the task's auto-generated field proposal, so expect to build cube_field rows
individually afterward (see below) — extra work for a wide table, but the only path that reliably
works.
Running the task, when it does work:
- Auto-generates a proposed set of
cube_fieldrows (dimensions and values) from the table's columns. - Changes the source table's screen type to a cube-capable screen type.
- Needs review, not blind acceptance — per the docs, remove semantic-free ID columns, confirm every value is sourced from the fact-grain table, and re-classify anything mis-detected as dimension vs. value.
cube_field has the same fallback need. Its declared navigation properties on cube
(detail_ref_cube_cube_field_dimension/_value, splitting fields by dimension vs. value) look like
the natural parent-nav path for adding one — verified live, both are rejected as invalid parent
targets for a staged add. Add cube_field with a plain top-level add (the full compound key supplied
directly) instead of trying to nest it under cube.
cube.default_cube_view_id sets which view opens by default; cube.allow_dragging_fields is the
model-level switch for the end-user "Cube panel" (drag-and-drop self-service rearranging — see
"User customization" below). cube.olap_connection/olap_server_name/olap_db_name/olap_cube_name
are legacy Windows-GUI-only OLAP cube settings (Microsoft Analysis Services) — irrelevant to a
Universal/web cube and only relevant when maintaining an old Windows application.
task_delete_cube removes the whole cube. task_unlink_generated_object (present on cube,
cube_field, cube_view, and more — every entity here carries generated_by_control_proc_id)
detaches a row that a control procedure generated, before manually editing it — the same
generated-object convention documented for scheduler/map components.
Naming trap: "dimension"/"measure" vs. "Dimension"/"Value"
cube_field.cube_field_type is a two-value enum: dimension (0) and measure (1). Every
UI screen, the docs, and the 2025.1 modeler split call the second one "Value", not "Measure" —
community feedback specifically flagged the old combined "Cube fields" tab as confusing before it
was split into separate Dimensions and Values tabs. When reading or writing
cube_field_type, use the enum literal measure; when talking to a user or naming things, say
"value." Don't let the schema's internal name leak into a field's user-facing description.
Cube fields — dimensions and values
Confirmed schema (cube_field):
| Field | Purpose |
|---|---|
cube_field_type |
dimension or measure (="Value" — see above) |
col_id |
Source column on the underlying table |
summary_type |
Aggregation: average, count, min, max, stddev, stddevp, sum, var, varp, formula, sql_expression — only meaningful for a value |
summary_display_type |
How the aggregated number is shown: default, percentage_of_column, percentage_of_row, percentage_of_variation, abs_variation |
no_of_decimals |
Display precision — set by business meaning, not raw DB scale |
editable |
Whether this value can become an editable pivot cell (see "Editable values" — several other preconditions also apply) |
grp_interval + type_of_grp_interval |
Enables and selects an interval/bucketing for a dimension: alphabetical, numeric, date, date_year, date_quarter, date_month, date_week_of_year, date_week_of_month, date_day_of_year, date_day_of_month, date_day_of_week, year_age, month_age, week_age, day_age |
grp_interval_numeric_range |
The bucket width, for numeric intervals |
cube_field_grp_id |
Hierarchy parent — see below |
Intervals
Use type_of_grp_interval to reduce cardinality on a raw date/number into something users can
actually scan: order date → date_year/date_quarter/date_month; lead time → numeric buckets
via grp_interval_numeric_range. Per the research, day-of-week numbering starts at 1 = Sunday —
confirm this matches user expectations before shipping a week-based view, and take particular care
with fiscal calendars, week numbering, and daylight-saving boundaries.
Hierarchies — cube_field_grp_id
Confirmed live: cube_field_grp_id is a self-referencing lookup to another cube_field on the
same cube. To nest "Product" under "Product group," set the Product field's own
cube_field_grp_id to the Product group field's cube_field_id — the child row points at its
parent, the same directional pattern as a normal FK column, not the inverted pattern variants use for
tree hierarchies. Once grouped this way, the child dimension is no longer independently offered
outside its parent's hierarchy — group dimensions only when every child genuinely has one meaningful
parent throughout the analyzed period; don't group two fields together merely because they're usually
shown side by side.
cube_field_grp_id alone does not make the hierarchy appear in a pivot. Verified live: wiring the
parent-child chain via cube_field_grp_id and placing only the leaf field in cube_area_category_row
produced a flat list with no nesting at all — every ancestor level was invisible. The fix: place
every level of the hierarchy, not just the leaf, as its own cube_view_field row in the same
area, in ancestor-to-leaf order (outermost first). cube_field_grp_id establishes the levels'
parent-child identity (and is what makes a grouped child stop being independently offered in the
field picker) — it does not by itself drive nested-row rendering from a single placement. Set
expand = true on the outermost level's cube_view_field row so the tree opens one level deep by
default rather than fully collapsed.
Calculated fields — cube_field_query
For summary_type = sql_expression (the current, non-deprecated mechanism — formula is the older
Windows-era equivalent), the actual expression lives in a separate per-RDBMS row:
cube_field_query, keyed by (cube_id, cube_field_id, rdbms_type) with a single formula text
field. This is how ratios and margins get computed after aggregation, referencing sibling cube
fields through the mandatory alias t1:
(t1.revenue - t1.cost) / NULLIF(t1.revenue, 0)
Write one cube_field_query row per RDBMS your branch targets (sqlserver, oracle, postgresql,
iseries). This is the correct way to build gross margin, average selling price (revenue/units),
utilization, completion percentage, and variance — always guard division by zero and null
propagation, and never average pre-computed row-level ratios when you can recompute them from
aggregated numerator/denominator instead.
Cube views
cube_view is one full pivot/chart configuration. Confirmed fields, grouped by concern:
Identity & visibility: cube_view_description, show_cube_view, cube_view_grp_id +
order_no/abs_order_no, cube_view_icon_id, screen_area_id, custom_display_type (the same
icon/text/overflow-fallback enum used by menu items and tasks — icon_text_*, text_only_*,
icon_only_overflow, overflow, or hidden). Set cube_view_icon_id to a suitable icon as part of
creating the view, per thinkwise_software_factory_icons — don't leave it unset by default.
Default presentation: default_cube_view_type — none, pivot_table, or chart.
Totals (pivot): show_col_grand_total/show_row_grand_total (an extra column/row presenting
the opposite axis's grand total), show_col_total/show_row_total (subtotals), total_position —
near or far. Show totals only where they're mathematically meaningful — a semi-additive or
non-additive value's grand total can be actively misleading.
Drill-down: drill_down_grid — double-clicking a pivot cell opens the underlying records,
still honoring the view's filters. Essential for trust in a number, but the underlying detail must
still be covered by the subject's own row-level authorization — drill-down is not a security
boundary by itself.
Chart settings: chart_type (a large enum — 2D/3D area/bar/line/pie/doughnut/funnel/bubble/
radar/gantt/candlestick and stacked/full-stacked/side-by-side variants of most of them),
chart_palette_id, chart_rotated, show_labels + label_position_bar (center/top) +
label_position_pie (inside/outside/two_columns), show_percentage, transparency (0–100 in
steps of 10), show_legend + legend_alignment_horizontal/legend_alignment_vertical +
legend_direction + legend_max_horizontal_percentage/legend_max_vertical_percentage. Universal
maps every 3D type to its 2D equivalent for rendering and folds a few specialized/unsupported types
down to columns — design for the supported Universal behavior, since 3D rarely helps comparison
anyway.
task_copy_cube_view/task_rename_cube_view/task_renumber_cube_view/task_delete_cube_view —
same create/rename/renumber/delete family as every other ordered child object in the model.
task_cube_view_mark_new_object_approved/_disapproved — the standard new-object review workflow
(also present on cube_field).
View groups — cube_view_grp
Groups views into a labeled submenu of the cube-view bar once there are enough views that a flat bar
becomes hard to scan (cube_view_grp_description, sub_menu flag, icon_id, custom_display_type,
order_no). Group by question or audience (Sales / Margin / Volume / Operations / Quality), not by
creation order. Set icon_id to a suitable icon representing the shared question/audience, per
thinkwise_software_factory_icons.
Field placement in a view — cube_view_field
Places one cube_field into exactly one area of exactly one cube_view. Confirmed cube_area enum:
| Value | Meaning |
|---|---|
cube_area_filter |
Restricts the view without becoming an axis |
cube_area_category_row |
Primary drill path — put the broadest dimension first |
cube_area_series_column |
Secondary axis — keep cardinality low or the pivot gets very wide / the chart legend gets illegible |
cube_area_value |
The aggregated number(s) shown |
cube_area_menu |
A fifth value present in the schema alongside the four documented UI areas; its exact rendering isn't covered by current docs/community material — likely the "not yet placed on any axis" pool feeding the field picker. Verify in the SF UI on a live row before treating it as equivalent to one of the four areas above. |
Setting the area: task_cube_view_field_area_drag_drop is confirmed live to take no
parameters on either binding (cube_view_field or the mirror cube_view_field_set_up) — it exists
to back the SF's own visual drag-and-drop panel, where the target area is implied by which UI
drop-zone triggered it. Via an MCP connector, don't try to call this task — just patch
cube_view_field.cube_area directly with stage_resource/patch_resource/commit_resource; it's
a plain field, not a snapshot-gated one. This field has also been seen silently dropping when set
together with other fields in one combined write (the general "last field in a combined write can
drop" quirk — see thinkwise_datamodeling_guidelines) — after patching cube_area, re-read the row
back and confirm the value actually stuck. Re-tested and fixed, verified live: setting cube_area
last among the properties in one combined stage_resource/patch_resource call (after order_no and
any other field being changed alongside it) avoided the drop entirely — both fields held correctly with
no follow-up patch needed. Order the properties this way instead of isolating cube_area into its own
call.
Other confirmed fields: order_no/abs_order_no (field order within its area), field_width
(pixels), sort_order (asc/desc) + sort_by_cube_field_id (sort this category/series by
another field's aggregated value — e.g. sort customers by revenue rather than alphabetically —
distinct from simply sorting the axis's own display value), show_top_x + type_of_show_top_x
(absolute/percentage) + show_other (fold everything outside the Top X into an "Other" bucket —
strongly prefer enabling show_other whenever show_top_x is set, so users don't mistake a partial
ranking for the full total), expand (default expansion state — for a Year→Quarter→Month
hierarchy, expanding only the top level usually communicates the pattern better than expanding every
leaf).
Default filter values — cube_view_field_filter
A repeatable child row per allowed/default value, keyed by (cube_view_id, cube_field_id, filter_value) — only meaningful for a field placed in cube_area_filter. This is how a view's
default context (current company, recent years, completed-only transactions) gets modeled. Whatever
default filter you set here must remain visible and understandable to the user — a hidden
default filter that silently excludes canceled orders or old periods breaks reconciliation even when
the exclusion was intentional. Row-level security belongs in the subject/authorization layer, never
only in a cube view's default filter — a saved filter is a user convenience, not an access control.
Extra totals — cube_view_field_total
Keyed by (cube_field_id, summary_type) — a value field can carry more than one total row, each
with its own summary_display_type/no_of_decimals/order_no. This is distinct from the cube
field's own base summary_type (used for each leaf cell): a total row can legitimately use a
different aggregation than the cell aggregation for a subtotal/grand-total presentation (e.g. an
average unit price at the leaf cell, but the total row instead shows a sum of the underlying
quantity via a second cube field, or the same value totaled with a different display type). Add a
total row deliberately per value, and treat a semi-additive or non-additive value's total with
extra scrutiny — a naive sum total row on a balance or a ratio field looks fine and is wrong.
Conditional layouts on a cube view
cube_view_field_conditional_layout — a cube-specific conditional-layout family (not a row in
the generic conditional_layout entity the thinkwise_software_factory_conditional_layouts skill
covers). Confirmed fields: condition_cube_field_id (which field the condition evaluates),
numeric_condition (equal_to, not_equal_to, greater_than[_or_equal_to],
smaller_than[_or_equal_to], between/not_between, is_empty/is_not_empty), value/
until_value, and four independent apply-to flags — apply_to_cell, apply_to_total_cell,
apply_to_custom_total_cell, apply_to_grand_total_cell — plus the same
color/font/bold/italic/underline/strikethrough/font_size formatting fields as the generic mechanism,
each split into light/dark theme variants. Test each apply-to flag independently: a condition
that's meaningful on a leaf cell (e.g. "utilization above 95%") is frequently misleading when the
same rule also colors a grand total. This mechanism applies only to modeled standard cube views, not
ad hoc views end users build for themselves via the Cube panel.
Chart extras — constant lines and custom series colors
cube_view_constant_line— a fixed threshold/target line drawn on a chart'sx_axisory_axisat a givenvalue, withcolor,thickness,dash_style, optional title (show_title/title_color/title_font_id/title_alignment),show_in_front_or_behindthe data, andshow_in_legend. Use for SLA thresholds, budget targets, or capacity limits overlaid on a trend.chart_legend_color— overrides the palette's automatic color for one specific value member (cube_field_id+color_light/color_dark), letting e.g. a fixed red always represent "Returns" regardless of palette rotation.
For making pivot values directly editable (preconditions, and the self-referencing-hierarchy
structural trap), read references/editable_values.md before enabling this.
Permissions — role_cube_overview / role_cube_field_overview
Confirmed live, both role-scoped and independent of a table's own row/column rights:
| Entity | Key fields | Purpose |
|---|---|---|
role_cube_overview |
available |
Whether this role can see/use the cube at all |
dragging_fields_granted (with meta-mirror sf_allow_dragging_fields) |
Whether this role gets the end-user Cube panel (self-service rearrange), independent of whether the cube-level cube.allow_dragging_fields switch is even on |
|
role_cube_field_overview |
available |
Whether this role sees this specific dimension/value at all |
editable (with meta-mirror sf_editable) + granted |
Whether this role can edit this specific value — on top of every other editable-value precondition above |
Both carry rights_icon (super_user/grant/read/hidden/unauthorized) mirroring the standard
rights model used everywhere else. A cube can leak information through aggregated totals even when
individual rows stay hidden — apply and test subject/row-level authorization, cube and cube-field
availability, edit rights, drill-down access, and export permissions together, not any one of them
in isolation. Filters and hidden fields are UI conveniences, never a substitute for these rights.
Screen types, components, and per-variant overrides
The default cube-capable screen types are cube, cube_horizontal, and cube_no_fields; the
available components are Pivot table, Chart, Cube panel, and Cube view bar — assigning
these to a table's screen type follows the exact same mechanics as any other screen type (pick from
what already exists in the model; creating a new screen type isn't supported via MCP — see
thinkwise_software_factory_build_planner). The platform removes the Pivot table component
automatically whenever no cube definition exists for the effective subject — if a screen looks like
it's missing its pivot, check that a cube row actually exists for that table before assuming a
component-wiring bug.
Per-table-variant overrides — confirmed live
Same field-level inheritance pattern documented generically in thinkwise_software_factory_variants
(no Setup/snapshot step — plain override, tracked until it diverges):
tab_variant_cube_overview(cube_id+tab_variant_id) — per-variantdefault_cube_view_idandallow_dragging_fields;task_reset_tab_variant_cube_overviewreverts to inheriting the default.tab_variant_cube_view_overview(cube_id+tab_variant_id+cube_view_id) — per-variantshow_cube_view,screen_area_id,custom_display_type,conditional_layout_code;task_reset_tab_variant_cube_view_overviewreverts it.
Use these when different audiences of the same table need a different default view or a different subset of visible views (e.g. an executive variant showing only two high-level views vs. an analyst variant showing all of them with the Cube panel enabled) — not a reason on its own to build a second cube.
User customization — the Cube panel
When cube.allow_dragging_fields (or its per-variant override) is on and the role has
dragging_fields_granted, end users can rearrange fields between areas, change sort/chart settings,
and save their own personal cube views with their own filters — without touching the model. Modeled
views should still stand on their own as good starting points; a personal view can go stale when the
underlying cube fields change later, so treat this as a reason to periodically review, not a reason
to under-invest in the modeled views.
Known SF-modeling friction (from Thinkwise Community feedback)
Worth knowing going in, since these are reported pain points rather than bugs in your modeling:
- No live preview in the Software Factory. Cube views can't be previewed while modeling — the documented workaround is building the view once in the actual running application, then replicating that field arrangement back into the SF. Thinkwise has stated (per a 2024 community reply) that a write-back/WYSIWYG editor from the running app into the SF is a longer-term direction, not something shipped yet — don't assume a preview or write-back capability exists.
- Interval auto-mapping can misfire — community reports of week/month/day interval detection
picking the wrong granularity on the generated proposal. Always manually verify
type_of_grp_intervalon every interval dimension rather than trusting the generated default. - The 2025.1 release specifically addressed several of these complaints: cube fields are now
split into separate Dimensions/Values tabs (matching the
dimension/measuresplit above), and a visual "Cube set-up panel" (thecube_view_field_set_upentity, drag-drop area assignment) was added. If working against an older branch/model version, some of this may not be present yet. - Users have reported the four area names (Filters/Categories-Rows/Series-Columns/Values) not always
matching what they expected from the running app's terminology, and that assigning a field to one
area can visually surface elsewhere unexpectedly — when a placement looks wrong after a
patch_resourceoncube_area, re-querycube_view_fieldto confirm the stored value rather than trusting a stale prior read.
Recommended workflow — new cube from scratch
- Write down the analytical question and audience in one sentence.
- Identify the source subject and state its exact row grain; validate independently (control totals) that joins behind it don't duplicate facts.
task_enrichment_create_cubewithtab_id(+ optionalcube_description) to generate the starting proposal.- Review every generated
cube_field: remove semantic-free IDs, fix any mis-classified dimension/value, confirmcol_idsources are fact-grain. - Gate: present the proposed dimension/value classification, hierarchy plan, and view list to the
user, and get their explicit confirmation before proceeding to any further staged writes
(interval/hierarchy wiring, additional views, etc.) — don't treat step 4's review as silent
authorization to keep building. This is the
thinkwise_software_factory_mcp_base"Confirm-before-mutate" convention applied at this skill's own grain. - Set
type_of_grp_interval/grp_interval_numeric_rangeon date/numeric dimensions that need bucketing; wirecube_field_grp_idfor genuine hierarchies. - For ratios/margins, add a
cube_fieldwithsummary_type = sql_expressionand onecube_field_queryrow per target RDBMS, using thet1alias. - Create one or more focused
cube_viewrows — each answering one question, not an everything-view. - Add
cube_view_fieldrows placing each relevantcube_fieldintocube_area_filter/cube_area_category_row/cube_area_series_column/cube_area_value— patchcube_areadirectly rather than via the drag-drop task. - Configure
cube_view_field_filterdefaults,sort_order/sort_by_cube_field_id,show_top_x/show_other, andexpandper field. - Configure
cube_viewtotals (show_*_total,total_position),drill_down_grid, anddefault_cube_view_type(pivot vs. chart). - For a chart view:
chart_type,chart_palette_id, labels/legend, and anycube_view_constant_line/chart_legend_coloroverrides. - Add
cube_view_field_conditional_layoutsparingly, testing each apply-to-cell/total/grand-total flag separately. - Decide
cube.allow_dragging_fieldsand per-roledragging_fields_granted; grantrole_cube_overview/role_cube_field_overviewrights. - Only enable
editableon a value after confirming every precondition in "Editable pivot values" holds for its intended grain. - Wire the table's screen type/components — picking an existing cube-capable screen type, since
creating a new one isn't supported via MCP (see
thinkwise_software_factory_build_planner) — and any per-variant overrides (tab_variant_cube_overview/tab_variant_cube_view_overview). - Validate totals against an independently calculated control total, check performance at production-like cardinality, and test light/dark conditional-layout themes.
Practical example — a sales revenue cube
Source subject: sales_order_line, grain = one invoiced order line.
- Dimensions:
order_date(intervaldate_month, hierarchy:order_date_yearascube_field_grp_idparent oforder_date_quarter, which is in turn the parent oforder_date_month),customer_id,product_idgrouped underproduct_group_id(cube_field_grp_idon the product field points at the product-group field),region_id. - Values:
revenue(summary_type = sum),quantity(sum),unit_price(summary_type = sql_expression,cube_field_query.formula = t1.revenue / NULLIF(t1.quantity, 0)— never a plainaverageof the stored unit price column, which would misweight lines of different quantity). - Views:
- Revenue trend —
order_date_monthincube_area_category_row,revenueincube_area_value,default_cube_view_type = chart,chart_type = 2d_line. - Revenue by customer and product —
customer_idincube_area_category_row,product_group_idincube_area_series_column,revenue+quantityincube_area_value,show_top_x = 10withshow_other = truesorted byrevenue(sort_by_cube_field_id), pivot presentation withshow_row_total/show_col_grand_totalon.
- Revenue trend —
- Conditional layout:
condition_cube_field_id = revenue,numeric_condition = smaller_than,value = 0,apply_to_cell = true, red background — flags a reversed/negative order line at a glance. - Rights:
role_cube_overview.available = truefor Sales role;dragging_fields_granted = trueonly for the Analyst role;role_cube_field_overview.editableleftfalseeverywhere — this is a historical-analysis cube, not a planning input.
Pre-flight checklist
- State the grain and validate control totals before creating anything — a technically correct
sumover duplicated rows is still a wrong business answer. task_enrichment_create_cubecan reject a not-yet-existing cube's key outright — verified live; don't spend more than one retry on it. Fall back to a plain add oncube(and, separately, oncube_field— its declared dimension/value navs fromcubearen't valid parent-nav targets either).- Review the generated proposal — never accept it unchanged. Remove ID-only fields, confirm fact-grain sourcing, re-check every dimension/value classification.
cube_field_typeenum literal ismeasure; call it "Value" everywhere user-facing — don't let the schema name leak into descriptions or conversation.cube_field_grp_idis a self-referencing parent pointer — set it on the child dimension to build a hierarchy; this is the opposite direction of variant-treeparent_col_iddocumented in the variants skill, so don't assume the two work the same way.- Wiring
cube_field_grp_idis not enough to see a hierarchy in the pivot — every level, not just the leaf, needs its owncube_view_fieldplacement in the same area (ancestor-to-leaf order), or the tree renders flat with no nesting at all. - A self-referencing hierarchy (org chart, BOM, category tree) sharing its table with an editable
value is a structural trap, not just a permissions edge case — a parent with exactly one child can
look editable and silently write into the child's row instead. Give the hierarchical/browsing view a
second,
editable = falsefield on the same column; keep the real editable field only in a separate flat view. - Calculated/ratio fields live in
cube_field_query, one row per RDBMS, aliast1— never as a plain average of a stored ratio column. - Set
cube_areaoncube_view_fielddirectly viapatch_resource— thetask_cube_view_field_area_drag_droptask carries no parameters and exists for the SF's own visual panel, not for programmatic placement. show_top_xshould almost always pair withshow_other = true— a partial ranking without "Other" looks like the complete picture.- An editable value needs all six preconditions at once (permission + is-a-value + column
editable + aggregation in {min,max,sum,average} + exactly-one-row-per-cell + the per-role
editablegrant) — missing any one silently keeps the cell read-only or produces an ambiguous write-back. cube_view_field_conditional_layoutis a separate entity family from the genericconditional_layouttable — don't look for cube formatting rules there.- Check
role_cube_overview/role_cube_field_overviewindependently of subject rights — a cube can expose an aggregate over rows a role couldn't individually see; totals are not automatically safe just because row-level security exists elsewhere. - No live preview exists in the SF for a cube view — expect to verify the real arrangement in the running application, especially on an older branch predating the 2025.1 set-up panel.