Labsco
microsoft logo

fabriciq-ontology-authoring-cli

✓ Official729

by microsoft · part of microsoft/skills-for-fabric

Create and evolve Fabric IQ Ontology (preview) items from CLI — define entity types, properties (including timeseries), relationship types, and bind them to OneLake lakehouse tables (static + timeseries) or Eventhouse / KQL database tables (timeseries only). Uses the Fabric item-definition REST API (Create Item / Update Item Definition) with `InlineBase64` parts. Use to create a Fabric Ontology item; add or alter entity types, properties, or keys; add timeseries properties and bindings; bind an

🧩 One of 7 skills in the microsoft/skills-for-fabric package — works on its own, and pairs well with its siblings.

This is the playbook your agent receives when the skill activates — you don't need to read it to use the skill, but it's here to audit before installing.

Update Check — ONCE PER SESSION (mandatory) The first time this skill is used in a session, run the check-updates skill before proceeding.

  • GitHub Copilot CLI / VS Code: invoke the check-updates skill (e.g., /fabric-skills:check-updates).
  • Claude Code / Cowork / Cursor / Windsurf / Codex: read the local package.json version, then compare against remote via git fetch origin main --quiet && git show origin/main:package.json (or the GitHub API). If remote is newer, show the changelog and update instructions.
  • Skip if the check was already performed earlier in this session.

CRITICAL NOTES

  1. Ontology is preview. The item type value is Ontology. Features and wire format may change; validate against the current docs before production use.
  2. To find the workspace details (including its ID) from workspace name: list all workspaces and use JMESPath filtering.
  3. To find the item details (including its ID) from workspace ID, item type (Ontology), and item name: list all items of that type in that workspace and use JMESPath filtering.
  4. Authoring a relationship type requires two distinct entity types that already exist in the ontology. The source.entityTypeId and target.entityTypeId values are the entity type IDs you assigned, not item IDs.
  5. Data bindings reference a source table by workspaceId, itemId, sourceTableName, and — for lakehouse sources — sourceSchema. Lakehouse (LakehouseTable) sources carry the lakehouse item ID; Eventhouse (KustoTable) sources carry the Eventhouse item ID plus clusterUri and databaseName. Key column(s) on the source side must match the entity type's key property(ies). Eventhouse sources are TimeSeries-only; the static (NonTimeSeries) binding must come from a lakehouse.

fabriciq-ontology-authoring-cli — Fabric Ontology Authoring via CLI

Table of Contents

TaskReferenceNotes
Finding Workspaces and Items in FabricCOMMON-CLI.md § Finding Workspaces and Items in FabricMandatory — resolve workspace/item IDs before authoring
Fabric Topology & Key ConceptsCOMMON-CORE.md § Fabric Topology & Key ConceptsWorkspace → Item hierarchy
Authentication & Token AcquisitionCOMMON-CORE.md § Authentication & Token AcquisitionUse https://api.fabric.microsoft.com audience for control plane
Core Control-Plane REST APIsCOMMON-CORE.md § Core Control-Plane REST APIsCreate Item, Get/Update Item Definition
Long-Running Operations (LRO)COMMON-CORE.md § Long-Running Operations (LRO)Item create/update returns an LRO
Rate Limiting & ThrottlingCOMMON-CORE.md § Rate Limiting & Throttling
Authentication RecipesCOMMON-CLI.md § Authentication Recipesaz login; token acquisition
Fabric Control-Plane API via az restCOMMON-CLI.md § Fabric Control-Plane API via az restAlways pass --resource https://api.fabric.microsoft.com
Long-Running Operations (LRO) PatternCOMMON-CLI.md § Long-Running Operations (LRO) PatternFor Ontology create/update, poll /v1/operations/{x-ms-operation-id} on the Fabric host — see LRO Header Capture
Gotchas & Troubleshooting (CLI-Specific)COMMON-CLI.md § Gotchas & Troubleshooting (CLI-Specific)Token audience, shell escaping
az rest TemplateCOMMON-CLI.md § az rest Template
Definition Envelope (parts, payloadType)ITEM-DEFINITIONS-CORE.md § Definition EnvelopeInlineBase64 parts pattern used for Ontology
Ontology Definition ReferenceONTOLOGY-AUTHORING-CORE.md § Definition TreeAuthoritative file/folder layout for the ontology item
EntityType & EntityTypeProperty schemaONTOLOGY-AUTHORING-CORE.md § EntityType fileAllowed valueType values, key constraints, name regex
DataBinding schema + source-type mappingONTOLOGY-AUTHORING-CORE.md § DataBinding fileLakehouse & Eventhouse shapes; value-type mapping; binding rules
RelationshipType + Contextualization schemaONTOLOGY-AUTHORING-CORE.md § RelationshipType fileSource/target constraints, link table requirements
Ontology ConceptsSKILL.md § Ontology Item ConceptsEntity types, properties, bindings, relationship types
Tool StackSKILL.md § Tool Stack
ConnectionSKILL.md § ConnectionDiscover workspace, lakehouse, ontology IDs
Authoring ScopeSKILL.md § Authoring ScopeSupported operations at a glance
Authoring Mechanics (full reference)authoring-mechanics.mdEnvelope, IDs, create, entity types, bindings, relationships, update, verify
Worked Examplesexamples.mdEnd-to-end bash recipes (create → bind → relationship → timeseries)
Preview & Confirm (mandatory before LRO write)preview-and-confirm.mdASCII proposal (greenfield) / change-set diff (brownfield)
Script Templatesdefinition-script-templates.mdBash / PowerShell fetch-mutate-send scaffolds
Must / Prefer / Avoid / TroubleshootingSKILL.md § Must / Prefer / Avoid / TroubleshootingLLM decision rules
Agentic WorkflowsSKILL.md § Agentic WorkflowsExploration-before-authoring, script generation
Agent Integration NotesSKILL.md § Agent Integration NotesHow this skill composes with agents / other skills

Ontology Item Concepts

A Fabric Ontology item is authored as a tree of JSON files inside the item definition. Each file is carried as a part in the parts[] array of the Create/Update definition envelope (payloadType InlineBase64).

ConceptDefinition file pathPurpose
Ontology envelopedefinition.jsonEmpty {}; required
Platform metadata.platform{ "metadata": { "type": "Ontology", "displayName": "<name>" } }
Entity typeEntityTypes/{entityTypeId}/definition.jsonName, namespace, key(s), display name property, properties[], timeseriesProperties[]
Entity type data bindingEntityTypes/{entityTypeId}/DataBindings/{guid}.jsonMaps a lakehouse or eventhouse table to properties; dataBindingType = NonTimeSeries or TimeSeries. Eventhouse (KustoTable) sources are allowed only for TimeSeries
Entity type documentsEntityTypes/{entityTypeId}/Documents/{name}.jsonOptional doc links
Entity type overviewsEntityTypes/{entityTypeId}/Overviews/definition.jsonOptional widgets layout
Entity type resource linksEntityTypes/{entityTypeId}/ResourceLinks/definition.jsonOptional Power BI / item links
Relationship typeRelationshipTypes/{relTypeId}/definition.jsonSource + target entity type IDs, name
Relationship contextualizationRelationshipTypes/{relTypeId}/Contextualizations/{guid}.jsonSource/target key bindings onto a lakehouse table

Property valueType allowed values (exact): String, Boolean, DateTime, Object, BigInt, Double. Use BigIntnot Int64 — for integers; there is no Guid value type (model GUIDs as String). Timeseries bindings require a timestamp column (source type datetime / date / timestamp) and a TimeSeries binding with timestampColumnName. See ONTOLOGY-AUTHORING-CORE.md § EntityTypeProperty for the full source-column → valueType mapping.

⚠️ Property names must be unique across both properties[] and timeseriesProperties[] within a single entity type. If a lakehouse table and an Eventhouse table both contain a column with the same name (e.g., tenant_id), you must rename one of the ontology property names to avoid a collision. The sourceColumnName in the binding can still point to the original column — only the ontology property name must be unique. For example, keep the static property as TenantId and name the timeseries one TsTenantId.

⚠️ Property names with the same name across different entity types must share the same valueType — the ontology enforces name-level type consistency across the entire definition. If SerialNum is String on one entity type, it cannot be BigInt on another. Either use the same valueType everywhere, or disambiguate with a prefix (e.g., SerialNumStr vs SerialNumInt).

⚠️ Part paths must always use forward slashes (EntityTypes/{id}/definition.json), never backslashes. On Windows, PowerShell path-joining operators (Join-Path, \) produce backslashes that the Fabric API rejects with ALMOperationBadRequest. Always build part paths with string interpolation using /.


Tool Stack

Ontology authoring uses the same Fabric control-plane tool stack as every other CLI skill — see COMMON-CLI.md § Tool Selection Rationale for the canonical list (install commands, prerequisite checks, base64 helpers, JSON tooling) and COMMON-CLI.md § Authentication Recipes for az login + token acquisition.

Ontology-specific tool guidance below covers only the gotchas that hit createItem / updateDefinition payloads.

⚠️ PowerShell ConvertTo-Json Warning: PowerShell's ConvertTo-Json can silently reorder keys and serialize $null differently than JSON null, which can cause ALMOperationImportFailed errors on updateDefinition. To avoid this:

  1. Always use [System.IO.File]::WriteAllText with [System.Text.UTF8Encoding]::new($false) to write JSON files — Out-File and Set-Content add a BOM that corrupts the payload.
  2. Build JSON with jq instead of ConvertTo-Json where possible — jq -nc produces deterministic, compact JSON without PowerShell serialization quirks:
    $json = '{}' | jq -nc --arg id "$ET_ID" --arg name "Site" '{id:$id,name:$name}'
  3. Validate the JSON before sending: Get-Content envelope.json | jq . — if jq fails, the payload is malformed.
  4. Use -Depth 10 on ConvertTo-Json — the default depth of 2 silently truncates nested objects.

⚠️ Avoid certutil -encode for InlineBase64 parts. Its output is line-wrapped with a header/footer and must be post-processed before use. On Windows, use PowerShell's [Convert]::ToBase64String([IO.File]::ReadAllBytes($path)) instead.


Connection

Ontology authoring targets the Fabric control plane. Before composing the definition you need: WS_ID (workspace), LH_ID (lakehouse item ID for static + lakehouse-timeseries bindings), and — for Eventhouse-backed timeseries — the Eventhouse item ID, KQL cluster URI, and KQL database name.

The Ontology-specific resolution gotchas (folder GUID vs. portal numeric ID, Eventhouse ID field mapping, schema discovery for bindings, LRO header capture on the preview redirect host) follow.

Folder (Ontology-specific gotcha)

The folderId field on the Ontology create payload requires the folder GUID — not the numeric subfolderId shown in portal URLs. Passing the portal number fails with 400 InvalidParameter … cannot convert "<numeric>" to Guid … Path 'folderId'. Resolve the GUID by listing GET /v1/workspaces/{WS_ID}/folders (per COMMON-CLI.md § Finding Workspaces and Items in Fabric) and filter by displayName.

Eventhouse / KQL Database (for TimeSeries bindings)

Ontology TimeSeries bindings backed by Eventhouse require three fields in the KustoTable data-binding payload — sourced from the KQL database record returned by GET /v1/workspaces/{WS_ID}/kqlDatabases:

KustoTable binding fieldSource field on the KQL-database record
itemIdproperties.parentEventhouseItemIdthe Eventhouse item ID, not the KQL database's own id
clusterUriproperties.queryServiceUri
databaseNamedisplayName (use the canonical casing returned by the API)

Eventhouse tables can back TimeSeries bindings only. The entity type's static (NonTimeSeries) binding must still come from a managed lakehouse table.

Common mistake: passing the KQL database's own id as the KustoTable.itemId. Use parentEventhouseItemId from the same record.

For the generic kqlDatabases listing call (pagination + JMESPath filter by displayName), see COMMON-CLI.md § Finding Workspaces and Items in Fabric.

Schema Discovery

Before composing bindings, discover the source table schemas so you map the correct column names. Use companion skills for schema discovery — they are faster and more reliable than raw REST calls.

  • Lakehouse tables → route to the sqldw-consumption-cli skill and query INFORMATION_SCHEMA.COLUMNS against the lakehouse SQL endpoint (returns all tables + columns in one query). If unavailable, fall back to the Fabric Tables REST API plus the OneLake Table API for Iceberg metadata.
  • Eventhouse / KQL tables → route to the eventhouse-consumption-cli skill and run .show database schema as json (returns every table + column in a single response). For a single table, use .show table <name> schema as json.

Use the column types returned here to fill valueType on each EntityTypeProperty — see the source-column → valueType mapping in ONTOLOGY-AUTHORING-CORE.md § EntityTypeProperty.

LRO Header Capture with az rest

az rest does not expose response headers by default. Both createItem and updateDefinition return 202 Accepted with an x-ms-operation-id header (and a Location header). Use --verbose and parse stderr to capture the operation id:

Prefer polling with x-ms-operation-id over the raw Location header for Ontology create/update. The public Fabric LRO contract supports polling either the Location header or https://api.fabric.microsoft.com/v1/operations/{operationId} (from x-ms-operation-id). In current Ontology preview behavior, observed Location values point at an analysis.windows.net redirect host (e.g. https://df-…-redirect.analysis.windows.net/v1/operations/{id}), not api.fabric.microsoft.com; polling that URL with az rest --resource https://api.fabric.microsoft.com re-authenticates against the wrong audience and fails with 401/403 — which hides the LRO status/error and leads to blind-retry loops. So capture x-ms-operation-id and poll https://api.fabric.microsoft.com/v1/operations/{operationId} (Fabric host, Fabric token). If you do follow Location, use the audience that URL requires.

# Bash — capture x-ms-operation-id from az rest --verbose stderr
OP_ID=$(az rest --method POST \
  --url "https://api.fabric.microsoft.com/v1/workspaces/${WS_ID}/items" \
  --resource "https://api.fabric.microsoft.com" \
  --headers "Content-Type=application/json" \
  --body @envelope.json --verbose 2>&1 \
  | grep -oiP "(?<=x-ms-operation-id': ')[^']+")

# Poll the Fabric operations endpoint (stays on api.fabric.microsoft.com)
while :; do
  OP=$(az rest --method GET \
    --url "https://api.fabric.microsoft.com/v1/operations/${OP_ID}" \
    --resource "https://api.fabric.microsoft.com")
  STATUS=$(printf '%s' "$OP" | jq -r .status)
  case "$STATUS" in
    Succeeded) break ;;
    Failed|Cancelled)
      # Read the error and FIX the payload — never blind-retry the same body.
      printf '%s' "$OP" | jq -r '.error | "\(.errorCode // .code): \(.message)"' >&2
      exit 1 ;;
    *) sleep 5 ;;
  esac
done
# PowerShell — capture x-ms-operation-id from az rest --verbose stderr
$result = az rest --method POST `
  --resource "https://api.fabric.microsoft.com" `
  --url "https://api.fabric.microsoft.com/v1/workspaces/$WS_ID/items" `
  --headers "Content-Type=application/json" `
  --body "@envelope.json" --verbose 2>&1
$opId = ($result | Select-String -Pattern "x-ms-operation-id': '([^']+)'" |
  ForEach-Object { $_.Matches[0].Groups[1].Value })

# Poll the Fabric operations endpoint (stays on api.fabric.microsoft.com)
do {
  Start-Sleep -Seconds 5
  $op = az rest --method GET `
    --url "https://api.fabric.microsoft.com/v1/operations/$opId" `
    --resource "https://api.fabric.microsoft.com" | ConvertFrom-Json
} while ($op.status -notin 'Succeeded','Failed','Cancelled')
if ($op.status -ne 'Succeeded') {
  # Read .error and FIX the payload — never blind-retry the same body.
  throw "createItem LRO $($op.status): $($op.error.errorCode) $($op.error.message)"
}

Important: createItem returns 202 with no response bodyaz rest exits with code 0 and prints nothing. This is normal. After the operation reaches Succeeded, list items to capture the new item ID. If status is Failed, the error field names the cause (e.g. a malformed entity-type part) — fix the payload and submit a corrected request rather than re-sending the same body.


Authoring Scope

OperationFabric REST CallDefinition Parts Touched
Create empty ontologyPOST /v1/workspaces/{ws}/items with type=Ontology.platform, definition.json
Add / alter entity typePOST /v1/workspaces/{ws}/items/{id}/updateDefinitionEntityTypes/{id}/definition.json
Bind entity type to table (non-timeseries)updateDefinitionEntityTypes/{id}/DataBindings/{guid}.json with NonTimeSeries
Bind entity type to table (timeseries)updateDefinitionEntityTypes/{id}/DataBindings/{guid}.json with TimeSeries + timestampColumnName
Add relationship typeupdateDefinitionRelationshipTypes/{id}/definition.json
Bind relationship (contextualization)updateDefinitionRelationshipTypes/{id}/Contextualizations/{guid}.json
Delete entity / relationshipupdateDefinition with that path omitted from parts
Rename ontologyupdateDefinition with updateMetadata=true and new .platform.platform

Update Item Definition replaces the full tree of included parts. Always fetch the current definition via Get Item Definition, mutate the parts locally, and resend the complete desired set.


Authoring Reference

Full JSON shapes, field contracts, and verification recipes for each operation live in authoring-mechanics.md. Worked end-to-end bash recipes live in examples.md. Use the sections below as a quick index:

TopicReference
Definition envelope (parts[], InlineBase64, base64 helpers)authoring-mechanics.md § Definition Envelope
ID generation (64-bit ints, GUIDs, name → id map)authoring-mechanics.md § ID Generation Pattern
Create empty ontologyauthoring-mechanics.md § Create the Ontology Item
Add an entity typeauthoring-mechanics.md § Add an Entity Type
Bind to lakehouse / eventhouseauthoring-mechanics.md § Bind an Entity Type
Relationship types + contextualizationsauthoring-mechanics.md § Add a Relationship Type
Apply a definition update (fetch → mutate → send)authoring-mechanics.md § Apply a Definition Update
Verify and inspectauthoring-mechanics.md § Verify and Inspect
Complete Bash / PowerShell scaffoldsdefinition-script-templates.md

Core invariants to keep in mind when authoring (full detail in the reference files):

  • Envelope shape: { "displayName", "type": "Ontology", "definition": { "parts": [ { "path", "payload", "payloadType": "InlineBase64" } ] } }; definition.json is literally {}; .platform carries metadata.type: "Ontology" + displayName.
  • IDs: entity / relationship / property IDs are positive 64-bit integers, data binding / contextualization IDs are GUIDs. Persist the name → id map in source control; never reuse an ID for a different concept.

ID map template — persist this alongside your deployment scripts (JSON or YAML):

{
  "ontologyName": "SkillTest_Fleet",
  "entityTypes": {
    "Site":      { "id": "1048860412765431174", "properties": { "SiteId": "1428056703884423742", "SiteName": "4251708967918658190" } },
    "Equipment": { "id": "3332700945676096991", "properties": { "EquipmentId": "4585483423451989345" } }
  },
  "relationshipTypes": {
    "EquipmentAtSite": { "id": "4242053467032157032" }
  },
  "bindings": {
    "Site_static":      "25e3a44a-b62a-40e3-a64a-a43caaa92d19",
    "Equipment_static": "5dc4cadd-3700-4c96-bb1e-41e4c909ae4d"
  }
}
  • Bindings: NonTimeSeries is lakehouse-only and at most one per entity type; a NonTimeSeries binding is required before any TimeSeries binding on the same entity type; TimeSeries can be lakehouse or Eventhouse; for KustoTable, itemId is the Eventhouse item ID (not the KQL database ID).
  • Relationships: source.entityTypeId and target.entityTypeId must be distinct and must reference entity types present in the parts tree.
  • Updates replace the included parts wholesale — always fetch the current definition, mutate locally, then send.

Agentic Workflows

⚠️ Do NOT generate monolithic .ps1 / .sh script files. Execute each step directly in the shell as individual commands. Generating a large script file introduces escaping bugs, parse errors, and property-access issues that are hard to debug. Instead:

  • Run az rest, jq, and PowerShell commands directly in the terminal
  • Build JSON payloads incrementally using jq -nc piped through variables
  • Write the final envelope to a temp file, then pass it to az rest --body @file
  • If a step fails, fix it and re-run — don't regenerate the entire script

Exploration Before Authoring

Greenfield vs brownfield execution strategy:

  • Greenfield (new ontology): Build the complete definition — entity types, bindings, relationships, contextualizations, timeseries — as a single createItem call with all parts in one envelope. This is faster and avoids intermediate states. The createItem payload accepts the full definition.parts[] array, not just .platform + definition.json.
  • Brownfield (updating existing): Execute incrementally — fetch the current definition, mutate, send. Verify with getDefinition after each updateDefinition to catch errors early. A failure partway through preserves prior progress.

Parallel Schema Discovery

When the ontology binds to multiple data sources (lakehouse tables + Eventhouse tables), discover schemas in parallel rather than sequentially. Launch separate discovery tasks that run concurrently:

┌─────────────────────────────────────────────────────────────────┐
│  ORCHESTRATOR (this skill)                                       │
│                                                                   │
│  Step 0 → Resolve workspace, folder, lakehouse ID, eventhouse ID │
│                                                                   │
│  Step 1 → Fan out schema discovery (parallel):                   │
│     ┌──────────────────────────┐  ┌────────────────────────────┐ │
│     │ TASK A: Lakehouse schemas │  │ TASK B: Eventhouse schemas │ │
│     │ sqldw-consumption-cli     │  │ eventhouse-consumption-cli │ │
│     │ or INFORMATION_SCHEMA     │  │ or .show database schema   │ │
│     │ → all tables + columns    │  │ → all tables + columns     │ │
│     └──────────┬───────────────┘  └──────────┬─────────────────┘ │
│                │                              │                   │
│  Step 2 → Merge schemas ◄────────────────────┘                   │
│     - Match entity tables (lakehouse) to telemetry (eventhouse)  │
│     - Detect property name collisions across sources             │
│     - Detect property type conflicts across entity types         │
│     - Rename collisions (e.g., TenantId → TsTenantId)           │
│                                                                   │
│  Step 3 → Propose model → PREVIEW & CONFIRM                     │
│                                                                   │
│  Step 4 → Build full envelope → createItem (single call)         │
└─────────────────────────────────────────────────────────────────┘

How to fan out (agent-specific):

  • GitHub Copilot CLI / Claude Code: launch two background task agents — one for lakehouse (sqldw-consumption-cli or INFORMATION_SCHEMA.COLUMNS query), one for Eventhouse (.show database schema as json). Read both results when they complete.
  • Single-threaded environments: run the two discovery queries sequentially — each is a single call, so the overhead is minimal.

The merge step (Step 2) is where most authoring bugs are caught — deduplicate property names, unify valueType across entities, and prefix timeseries properties that collide with static ones.

Detailed Step Flow

Step 0 → Is the request specific? Are the entity types, keys, and lakehouse tables named?
         → NO  → Ask: "Which entity types? What is the key of each? Which lakehouse table binds to each?
                  Any timeseries properties? Any relationships and their link tables?"
                  STOP — do not proceed until the user answers.
         → YES → Continue.
Step 1 → Resolve IDs: workspace, folder, lakehouse, eventhouse     [COMMON-CLI.md]
Step 2 → Discover source schemas (parallel where possible):
           a. Lakehouse: invoke `sqldw-consumption-cli` or query INFORMATION_SCHEMA.COLUMNS
           b. Eventhouse: invoke `eventhouse-consumption-cli` or run `.show database schema as json`
Step 3 → Merge schemas: detect property name collisions + type conflicts; rename as needed
Step 4 → If ontology exists: getDefinition → decode parts              (capture current IDs)
         Else: plan `createItem` with the FULL definition (all parts in one call).
Step 5 → For each entity type:
           a. Generate/reuse 64-bit IDs for entity + properties
           b. Build EntityTypes/{id}/definition.json
           c. Build one or two DataBindings/{guid}.json files
Step 6 → For each relationship:
           a. Confirm both entity types exist in Step 5 output
           b. Generate/reuse relationship type ID
           c. Build RelationshipTypes/{id}/definition.json
           d. Build RelationshipTypes/{id}/Contextualizations/{guid}.json
Step 7 → Base64-encode all parts; assemble envelope
Step 8 → **PREVIEW & CONFIRM** — render proposal (greenfield) or change-set diff (brownfield)
         and obtain explicit `yes` from the user. See [preview-and-confirm.md](https://github.com/microsoft/skills-for-fabric/blob/main/plugins/fabric-authoring/skills/fabriciq-ontology-authoring-cli/references/preview-and-confirm.md).
         Do not proceed on anything other than `yes`.
Step 9 → createItem OR updateDefinition (LRO)
Step 10 → Poll LRO until Succeeded; getDefinition; verify IDs and bindings; persist post-write snapshot for next-run diff

Script Generation Workflow

Step 1 → Capture user intent (entity types, keys, properties, relationships, source tables)
Step 2 → Save intent as a YAML/JSON spec in the consumer's repo — single source of truth
Step 3 → Generate: (a) the ID map, (b) per-file JSON parts, (c) the composite envelope
Step 4 → **PREVIEW & CONFIRM** — render proposal/diff and require explicit `yes`
         (see [preview-and-confirm.md](https://github.com/microsoft/skills-for-fabric/blob/main/plugins/fabric-authoring/skills/fabriciq-ontology-authoring-cli/references/preview-and-confirm.md)). The textual
         diff against the last-applied envelope snapshot feeds the brownfield change-set.
Step 5 → Apply via az rest --body @envelope.json (createItem or updateDefinition)
Step 6 → Poll LRO; on success, commit the envelope snapshot + ID map

Examples

End-to-end worked examples (create empty ontology → add entity type + non-timeseries binding → add relationship type + contextualization → add timeseries property + Eventhouse binding) live in examples.md. Complete fetch-mutate-send bash and PowerShell scripts live in definition-script-templates.md.


Agent Integration Notes

  • This skill is authoring-focused. Pair with a consumption skill (e.g., a Fabric Graph query skill) to validate the ontology end-to-end.
  • Parallelize schema discovery when the ontology binds to multiple source types:
    • Launch a background sqldw-consumption-cli task for lakehouse schemas (INFORMATION_SCHEMA.COLUMNS) — returns all tables + columns in one query.
    • Launch a background eventhouse-consumption-cli task for Eventhouse schemas (.show database schema as json) — returns all tables + columns in one call.
    • Both run concurrently. Merge results when both complete, then build the ontology model.
  • Merge step is critical — after discovery, deduplicate property names across properties[] and timeseriesProperties[], unify valueType for same-named properties across entity types, and prefix collisions before building the envelope.
  • When orchestrating multi-step customer workstreams that span Ontology + Eventhouse + Lakehouse, route via an agent (e.g., FabricDataEngineer) rather than chaining skills directly.
  • Reasonable upstream dependencies to assume: lakehouse tables already exist and have the key columns the user described. If not, the caller should invoke a lakehouse authoring skill first.