[Workflow] Organizing is a set of passes over the same objects. Doing only one or two of them (folders and sorting, say) leaves the model looking finished while names, descriptions and formats are still raw, so run every pass unless the user narrows the scope, and say which passes were skipped and why.
get_workspace_info first and confirm the model title with the user if it is not the one they named. Read with get_model_overview and get_object_details; write with execute_csharp_scriptte CLI (the te-cli skill) when there is no TE3 instance, for example against PBIP/TMDL on diskexecute_csharp_script call is one undo step and returns a property-level diff, so per-pass scripts keep every change reviewable and individually revertibleTableGroup, context), so read those back with a second scriptexecute_dax_query only exists while the TE3 MCP server has model data read permission. If it disappears mid-session, the client's tool list is stale; reconnect the server (/mcp). A calculated table that was never recalculated returns no data until it is refreshedIndexOutOfRangeException in ScrollbarIndicatorsController)Before changing anything:
lineage-analysis skill (or fab through the fabric-cli skill): reports bound to the model in every workspace, and composite models on top of it. Renames, deletions and the Analyze in Excel pass all depend on thistable.TableGroup (stored as the TabularEditor_TableGroup annotation). Use role-based groups such as Facts, Dimensions, Measures, Calculation Groups, Parameters. Groups only show in Tabular Editor; they do not reach Power BI, which is why table names must not carry DIM_/FACT_ prefixes instead/ only (Keys/Technical); one level is usually enoughTitle Case business names for tables, columns, measures and hierarchies (Order Date, Net Sales, Days of Supply), acronyms in capitals, no programming conventions, no technical prefixes, no unexplained abbreviations, and one consistent syntax for aggregations, units, periods and comparisons. The full rule set, the measure name construction order and the terminology questions to ask first are in references/naming-rules.md.
Customer Key, Customer, Cust for the same thing across tables collapse to one form; qualifiers that carry meaning (Billing Date, Order Date) stay. Pick singular or plural table names onceSourceColumn. Renaming the column object is enough while SourceColumn still matches an expression column; when the expression's names change, keep SourceColumn in step or the column is dropped on recalculationpbir-cli skill (local PBIR) or fabric-cli (service). Do not apply renames to a model with bound reports without that go-ahead. refactoring-renaming.md in the semantic-model skill has the full procedureAdd the natural drill paths: calendar (Year > Quarter > Month > Date), fiscal calendar if one exists, geography (Country > Region > City), product (Category > Subcategory > Product), organization. Skip a hierarchy whose levels are not truly nested.
Every text column with a non-alphabetical natural order gets a SortByColumn: month names by month number, month labels (Mar 2025) by a year-month key, weekday names by weekday number, statuses and buckets by an explicit order column. The sort key must have exactly one value per sort value, or refresh fails.
Hide what report authors should not drag into a visual: surrogate and foreign keys, sort keys, technical columns, and numeric fact columns meant to be aggregated only through a measure (hide them and make sure the measure exists). Hide tables that only support relationships or measures. Do not hide a column a hierarchy or visible slicer needs.
IsAvailableInMDX = false removes a column's attribute hierarchy: less processing and memory, and the column disappears from Excel PivotTables and other MDX clients.
TE3_BUILT_IN_SET_ISAVAILABLEINMDX_FALSE: hidden columns not used for sorting, in hierarchies, variations or calendars, without a sort-by column of their own, and not in a Direct Lake table. Run it with get_bpa_rule_resultsAnalyzedByExternalApplication events in the activity log. If Excel or other MDX use exists, tell the user which columns it would affect instead of disabling them silentlyEvery visible table, column, measure, hierarchy and function gets a description unless the name and metadata already say everything. As few words as possible, as many as necessary: only what the name does not already say (format, an example value, grain, exclusions, unit or currency, an acronym spelled out, what a measure aggregates). Never restate what the metadata already shows: the name, object type, table, source column, data type, format string, sort-by column, hierarchy levels, relationships or calculated status. A description or annotation that adds nothing beyond those stays empty.
Write descriptions and context annotations about 80% of the way to ASD-STE100 Simplified Technical English: short sentences, one fact per sentence, simple common words, active voice and present tense, no Latin abbreviations (Example:, not e.g.). Leave field names, product names, codes and format patterns as they are. In descriptions, put two or more facts on separate lines as - bullets; one fact stays a single line.
Month Label: "Example: Mar 2025"Month Sort: "- Format: YYYYMM\n- Example: 202503"Net Sales: "Invoice value after discounts and returns.\n- Local currency\n- Does not include freight or taxes"Format every visible measure and numeric column: separators, currency, percentage, decimals that match the data. Use a format string expression (FormatStringDefinition) when the format depends on context, such as the selected currency. Date formats are case-sensitive: MM is month and mm is minutes, so yyyy-MM-dd, never yyyy-mm-dd.
Set DataCategory where it changes client behavior: geography (Country, StateOrProvince, City, PostalCode, County, Continent, Latitude, Longitude, Place), WebUrl, ImageUrl, Barcode. The date table gets table-level DataCategory = "Time" and its date column IsKey = true.
Add an annotation named context (obj.SetAnnotation("context", "...")) to objects whose purpose neither a reader nor the metadata reveals: role-playing keys, helper tables, columns that exist for RLS or a calculation group, disconnected parameter tables, anything kept for a downstream dependency. A sort key needs none; SortByColumn already says what it sorts. A few words in the pass 7 style on why it exists, for example "Kept for the Finance export; not used in reports."
Each DAX calculated table and calculated column must justify itself in its context annotation, in one short sentence: why it is calculated in the model, why it must exist, and why it cannot come from the source (for example: derived from several fact tables at refresh time, depends on model relationships, or the source is a read-only view). If no reason holds, recommend moving it upstream rather than inventing one.
obj.FormatDax() formats at the end of the script_ (VAR _StartDate) and every temporary column created by ADDCOLUMNS, ROW, SELECTCOLUMNS or SUMMARIZECOLUMNS with @ ("@BusinessDate", referenced as [@BusinessDate]), so neither can be mistaken for a model table or columnFILTER on one column, aggregators instead of single-column iterators, VALUES instead of single-column SUMMARIZE, the two prefixes above, constant aggregations moved into variables, DISTINCTCOUNT, COALESCE, ISBLANK, IN instead of chained || equalities, no unused variables// line: a deliberate ALL that removes a filter users might expect to apply, a workaround for an engine quirk, a magic number's meaning. Do not comment the obviousLook for logic repeated across measures that one UDF could carry, propose it with the measures it replaces, and on approval create it with a compound name, type hints, and /// plus parameter comments. Syntax, naming, documentation layers and worked examples are in references/udfs.md.
A field that duplicates another (same values under a different name, a second copy of a key, an unused measure that repeats another's logic) can be deleted only if nothing uses it: no model dependency (ReferencedBy empty), no RLS or perspective reliance, and no report, Excel workbook or composite model in the pass 0 lineage. List deletions for the user before applying them.
get_bpa_rule_results) and fix what the passes introducedexecute_dax_query) to confirm sorting, formats and hierarchies behave