[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-explore-omni-omni-model-builder":3,"mdc--nc9uqy-key":40,"related-org-explore-omni-omni-model-builder":5802,"related-repo-explore-omni-omni-model-builder":5948},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":29,"repoUrl":30,"updatedAt":31,"license":32,"forks":33,"topics":34,"repo":35,"sourceUrl":38,"mdContent":39},"omni-model-builder","build Omni Analytics semantic models","Create and edit Omni Analytics semantic model definitions — views, topics, dimensions, measures, relationships, and query views — using YAML through the Omni CLI. Use this skill whenever someone wants to add a field, create a new dimension or measure, define a topic, set up joins between tables, modify the data model, build a new view, add a calculated field, create a relationship, edit YAML, work on a branch, promote model changes, or any variant of \"model this data\", \"add this metric\", \"create a view for\", or \"set up a join between\". Also use for migrating modeling patterns since Omni's YAML is conceptually similar to other semantic layer definitions.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"explore-omni","Explore Omni","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fexplore-omni.png","exploreomni",[13,17,20,23,26],{"name":14,"slug":15,"type":16},"Omni","omni","tag",{"name":18,"slug":19,"type":16},"YAML","yaml",{"name":21,"slug":22,"type":16},"CLI","cli",{"name":24,"slug":25,"type":16},"Data Modeling","data-modeling",{"name":27,"slug":28,"type":16},"Analytics","analytics",27,"https:\u002F\u002Fgithub.com\u002Fexploreomni\u002Fomni-agent-skills","2026-04-06T18:11:05.775943",null,3,[],{"repoUrl":30,"stars":29,"forks":33,"topics":36,"description":37},[],"A collection of skill for working with Omni. These skills help AI agents understand and execute Omni workflows more effectively.","https:\u002F\u002Fgithub.com\u002Fexploreomni\u002Fomni-agent-skills\u002Ftree\u002FHEAD\u002Fskills\u002Fomni-model-builder","---\nname: omni-model-builder\ndescription: Create and edit Omni Analytics semantic model definitions — views, topics, dimensions, measures, relationships, and query views — using YAML through the Omni CLI. Use this skill whenever someone wants to add a field, create a new dimension or measure, define a topic, set up joins between tables, modify the data model, build a new view, add a calculated field, create a relationship, edit YAML, work on a branch, promote model changes, or any variant of \"model this data\", \"add this metric\", \"create a view for\", or \"set up a join between\". Also use for migrating modeling patterns since Omni's YAML is conceptually similar to other semantic layer definitions.\n---\n\n# Omni Model Builder\n\nCreate and modify Omni's semantic model through the YAML API — views, topics, dimensions, measures, relationships, and query views.\n\n> **Tip**: Always use `omni-model-explorer` first to understand the existing model.\n\n## Prerequisites\n\n```bash\n# Verify the Omni CLI is installed — if not, ask the user to install it\n# See: https:\u002F\u002Fgithub.com\u002Fexploreomni\u002Fcli#readme\ncommand -v omni >\u002Fdev\u002Fnull || echo \"ERROR: Omni CLI is not installed.\"\n```\n\n```bash\n# Show available profiles and select the appropriate one\nomni config show\n# If multiple profiles exist, ask the user which to use, then switch:\nomni config use \u003Cprofile-name>\n\n# Confirm the active profile is authenticated and inspect your permissions:\nomni whoami whoami\n```\n\n> **Auth**: a profile authenticates with an **API key** or **OAuth**. If `whoami` (or any call) returns **401**, hand off — ask the user to run `! omni config login \u003Cprofile>` (OAuth 2.1 browser flow; it blocks ~2 min on the browser). Don't run `config login` yourself in a headless\u002FCI session (no browser → timeout); on a local interactive machine you *may*. See the **`omni-api-conventions`** rule for profile setup (`omni config init --auth oauth`) and discovering request-body shapes with `--schema`.\n\nYou need **Modeler** or **Connection Admin** permissions. Add `-o json` to any command to force structured output for parsing (default `auto` is human in a TTY, JSON when piped).\n\n## Omni's Layered Modeling Architecture\n\nOmni uses a **layered approach** where each layer builds on top of the previous:\n\n1. **Schema Layer** — Auto-generated from your database. Reflects tables, views, columns, and their types. Kept in sync via schema refresh.\n2. **Shared Model Layer** — Your governed semantic model. Where you define dimensions, measures, joins, and topics that are reusable across the organization.\n3. **Workbook Model Layer** — Ad hoc extensions within individual workbooks. Used for experimental fields before promotion to shared model.\n4. **Branch Layer** — Intermediate development layer. Used when working in branches before merging changes to shared model.\n\n**Key concept**: The schema layer is the source of truth for table\u002Fcolumn structure (refreshed when the database changes); all user-created content (dimensions, measures, relationships, topics) flows through the shared model layer. You build and modify it in **branches** (see \"Safe Development Workflow\" below) before merging back to the shared model.\n\n## Determine SQL Dialect\n\nBefore writing any SQL expressions, confirm the dialect from the connection — don't guess from the connection name:\n\n```bash\n# 1. List models to find connectionId\nomni models list\n\n# 2. Look up the connection's dialect\nomni connections list\n# → find your connectionId and read the \"dialect\" field\n# → e.g. \"bigquery\", \"postgres\", \"snowflake\", \"databricks\"\n```\n\nUse dialect-appropriate functions in your SQL (e.g. `SAFE_DIVIDE` for BigQuery, `NULLIF(a\u002Fb)` for Postgres\u002FSnowflake).\n\n## Schema Refresh: Syncing with Database Changes\n\nThe **schema layer** is auto-generated from your database. When your database schema changes (new\u002Fdeleted\u002Frenamed columns, type changes), refresh it to stay in sync: `omni models refresh \u003CmodelId>` (add `--branch-id \u003CbranchId>` to scope to a branch; requires **Connection Admin**).\n\nSee `references\u002Fschema-refresh.md` for when to trigger, what it does and its side effects, the deleted\u002Frenamed-column impact-check workflow, and connection\u002Fcredential error handling.\n\n## Known Issues & Safe Defaults\n\n- **Do not merge without explicit confirmation** — after branch validation and query testing, stop and ask the user before `omni models merge-branch`, even when the model is not git-connected. Treat requests like \"add a field\" or \"create a view\" as requests to prepare validated branch changes, not as permission to ship to production.\n- **Keep eval-created files on branches until confirmed** — if you create fields\u002Fviews for validation, report the branch name\u002Fid, validation status, and test query result. Only merge after the user explicitly says to merge, ship, publish, or promote.\n\n## Discovering Commands\n\n```bash\nomni models --help                # List all model operations\nomni models yaml-create --help    # Show flags for writing YAML\nomni models yaml-create --schema  # Print the body's JSON schema + a filled example (no token)\n```\n\n## Safe Development Workflow\n\nAlways work in a branch. Never write directly to production.\n\n### Step 0: Create a Branch\n\n```bash\nomni models create-branch \u003CmodelId> --name \"my-feature-branch\"\n```\n\nThe response `model.id` is your `branchId` — a UUID you'll pass to all subsequent API calls. To list existing branches at any time:\n\n```bash\nomni models list --include activeBranches\n```\n\n> **Git-connected models**: The repo is a *projection* of Omni's model for governance (pull requests, review, audit trail) — not the source of truth, and not a surface to author against. Omni's model state is authoritative, so model YAML hand-edited in the repo is validated only after you push, re-serialized to Omni's canonical form, and overwritten on the next regeneration. Author **through the Omni APIs on a branch** (via the Omni CLI, as below), then use `omni models commit` (Step 3 below) to sync the branch to git as a pull request and **review\u002Fmerge in your git provider** — don't hand-edit model YAML in the repo and push directly.\n\n> **⚠️ Never hand-edit model YAML in git for a git-connected shared model.** It can look correct in git for a while, but Omni regenerates the default branch from its own authoritative model state and **deletes git-only model files** that state doesn't contain — an observed customer incident, where model files committed only to git silently disappeared on Omni's next sync. All view \u002F topic \u002F relationship YAML must originate in Omni: edit on a branch → `omni models commit` → PR.\n>\n> **Model content vs. repo governance** — the split that makes this safe:\n> - **Omni model content** (view \u002F topic \u002F relationship YAML): author on an Omni branch → `omni models commit` → PR. Never commit it directly in git — Omni will regenerate over it.\n> - **Repo-governance \u002F non-model files** (a root `omni\u002FOWNERS.yaml`, CODEOWNERS, CI config, docs, scripts): not Omni model content, so direct git commits are fine — Omni's regeneration leaves them untouched.\n\n### Step 1: Write YAML to a Branch\n\n```bash\nomni models yaml-create \u003CmodelId> --body '{\n  \"fileName\": \"my_new_view.view\",\n  \"yaml\": \"dimensions:\\n  order_id:\\n    primary_key: true\\n  status:\\n    label: Order Status\\nmeasures:\\n  count:\\n    aggregate_type: count\",\n  \"mode\": \"extension\",\n  \"branchId\": \"{branchId}\",\n  \"commitMessage\": \"Add my_new_view with status dimension and count measure\"\n}'\n```\n\n> **Note**: The `branchId` parameter must be a UUID from the server (Step 0). Passing a string name instead will return `400 Bad Request: Unrecognized key: \"branchName\"`.\n\n> **⚠️ Editing an existing file? `fileName` is its exact path, not a regex (unlike on read).** Reuse the full-path key from your `yaml-get` response verbatim, including any folder prefix — e.g. `MARTS\u002Ffct_ai_events.view`, not `fct_ai_events.view`. A non-matching `fileName` doesn't error: Omni **silently creates a new file at that path** and returns `success: true`, so shortening the key produces a duplicate view at the repo root.\n\n> **Edits are whole-file writes — read-modify-write.** `yaml-create` **replaces** a file's authored content; it does not merge field-by-field. To change or add one field on an existing view, `yaml-get` the file first, edit it, and write the **complete** file back — otherwise the other authored fields are dropped. (Schema base columns are unaffected — they live in the schema layer, not the authored file.)\n\n> **Inspect a branch.** `yaml-get \u003CmodelId> --branchid \u003CbranchId>` **without** `--filename` enumerates the whole model — `--mode extension` returns only the files the branch **changed** (your deltas); `--mode combined` returns the **full composed** model (schema + shared + branch). Then drill into any file by its exact path.\n\n### Step 2: Validate and Test\n\nEvery YAML write must be validated and tested before merging — a field can be valid YAML yet produce wrong results or broken queries.\n\n```bash\n# 1. Validate — any issue with is_warning:false is a blocking error; fix before proceeding\nomni models validate \u003CmodelId> --branchid \u003CbranchId>\n\n# 2. Query the fields you changed — confirm no error and summary.row_count > 0\nomni query run --body '{\"query\":{\"modelId\":\"\u003CmodelId>\",\"table\":\"your_view\",\"fields\":[\"your_view.new_dimension\",\"your_view.new_measure\"],\"limit\":10,\"join_paths_from_topic_name\":\"your_topic\"},\"branchId\":\"\u003CbranchId>\"}'\n\n# 3. Read it back — confirm the field is present (and not duplicated at a second path)\nomni models yaml-get \u003CmodelId> --filename your_view.view --branchid \u003CbranchId>\n```\n\nSpot-check that values look right (a `sum` isn't returning a `count`; booleans read true\u002Ffalse), and if a field references another view include fields from both to confirm the join resolves. See `references\u002Fvalidation-and-testing.md` for join-path testing, natural-language validation via `omni ai job-submit`, the full results checklist, and duplicate-file recovery.\n\n### Step 3: Ship the Branch\n\n> **Important**: Always ask the user for confirmation before shipping. Changes applied to the production model cannot be easily undone. Only ship after validation and testing pass (Step 2).\n\nCheck whether the model is git-connected — `omni models git-get \u003CmodelId>`. A config with `sshUrl` \u002F `baseBranch` → git-connected → **Path A** (open\u002Fupdate a PR); a `404`\u002Fno config → not git-connected → **Path B** (merge directly in Omni).\n\n#### Path A — Git-connected: open or update a PR\n\nPush the branch contents to git. Creates a new git branch + PR if one doesn't exist; otherwise updates the existing PR:\n\n```bash\nomni models commit \u003CmodelId> --body '{\n  \"branch_id\": \"\u003CbranchId>\",\n  \"commit_message\": \"Add my_new_view with status dimension and count measure\"\n}'\n```\n\nSurface the returned `pr_url` to the user. The reviewer merges the PR in your git host; changes flow back to `baseBranch` on the next sync. Run `omni models commit --help` for optional body flags (`allow_branch_exists`, `require_branch_exists`) when you need to enforce open-only or update-only behavior.\n\n#### Path B — Not git-connected: merge in Omni\n\n```bash\nomni models merge-branch \u003CmodelId> \u003CbranchName>\n```\n\n#### After the merge — verify net-new topics and views (both paths)\n\nAfter the merge, Omni regenerates the default branch from its own (authoritative) model state — re-serializing to canonical form, and adding a normalization commit only when the merged git content *differs* from that state. Content authored through the Omni APIs is already canonical, so a clean merge often adds **no extra commit** — don't go hunting for one; verify by resolution instead. (A non-git merge promotes the branch into the shared model.) Either way — and **especially for net-new topics or views** — confirm the files resolve against the **production** model (no `--branchid`):\n\n```bash\n# 1. Files resolve in production — the new view appears \u002F the new topic resolves\nomni models get-views \u003CmodelId>                 # new view is listed\nomni models get-topic \u003CmodelId> \u003CtopicName>     # new topic resolves (base_view_name + join_via_map present)\n\n# 2. Production model validates — no blocking errors (any is_warning:false is blocking)\nomni models validate \u003CmodelId>\n\n# 3. Run at least one semantic query against the new topic\u002Fview\nomni query run --body '{\"query\":{\"modelId\":\"\u003CmodelId>\",\"table\":\"\u003Cbase_view>\",\"fields\":[\"\u003Cbase_view>.\u003Cfield>\"],\"limit\":10,\"join_paths_from_topic_name\":\"\u003CtopicName>\"}}'\n```\n\nIn the query response, confirm `summary.missing_fields` is `[]` (and `summary.invalid_calculations` is empty — note it comes back as `{}`, not `[]`). A non-empty `missing_fields` means a field didn't resolve — the signature of a model file dropped or renamed during the merge. If anything is missing, re-author it through Omni; never patch it back by hand in git.\n\n## YAML File Types\n\n| Type | Extension | Purpose |\n|------|-----------|---------|\n| View | `.view` | Dimensions, measures, filters for a table |\n| Topic | `.topic` | Joins views into a queryable unit |\n| Relationships | (special) | Global join definitions |\n\nWrite with `mode: \"extension\"` (shared model layer). To delete a file, send empty `yaml`.\n\n## Writing Views\n\n> **Every view that participates in joins MUST have a real `primary_key: true` dimension.** Without a genuine row-unique primary key, queries that join to this view can produce fanout errors or incorrect aggregations. Use the table's natural unique identifier (e.g., `id`, `order_id`, `user_id`). If no single column is unique, build a composite key from row-level columns that are jointly unique, for example `sql: ${order_id} || '-' || ${line_number}`. If you cannot define a row-unique expression, do not mark a dimension as `primary_key: true` yet; fix the grain first or avoid joining the view until a real key exists.\n\n> **Why the PK matters mechanically — symmetric aggregates.** Omni keeps `sum`\u002F`count`\u002F`avg` correct under a one-to-many join by **deduplicating on the primary key** ([symmetric aggregates](https:\u002F\u002Fdocs.omni.co\u002Fanalyze-explore\u002Fsql\u002Fsymmetric-aggregates)). A view **missing** a PK can't be made symmetric, so a `count`-of-rows measure on it **inflates** whenever a join duplicates its rows; a `count_distinct` measure survives only because it dedupes by construction. **Diagnostic heuristic:** when a measure exceeds a measure it should be a subset of (e.g. *\"sessions that viewed a product\" > \"total sessions\"*), suspect fanout from a non-distinct count on a view with **no\u002Fweak PK** — fix the PK, don't patch the number. Audit *every* base view for a PK (one missing PK is enough to break one viz). Note that a dashboard **filter\u002Fcontrol can pull in a join the topic never declared**, via the global `relationships` graph, to satisfy its field — so even a \"single-topic\" tile can fan out; the PK is what keeps the aggregates safe regardless.\n\n### Basic View\n\n```yaml\ndimensions:\n  order_id:\n    primary_key: true\n  status:\n    label: Order Status\n  created_at:\n    label: Created Date\nmeasures:\n  count:\n    aggregate_type: count\n  total_revenue:\n    sql: ${sale_price}\n    aggregate_type: sum\n    format: currency_2\n```\n\n### Understanding Schema Layer vs Extension Layer\n\nWhen you create a view, Omni separates **schema** (database structure) from **model** (your business logic):\n\n- **Schema layer**: Auto-generated base dimensions, one per column. Types come from the database. Read-only, synced via schema refresh.\n- **Extension layer**: Your custom YAML. Can override base dimensions, add new dimensions\u002Fmeasures, hide columns, add business logic.\n\nWhen both layers exist for a field with the same name, **your extension definition wins** but **type information comes from the schema layer**.\n\n**Example**: Table has columns `created_at` (DATE) and `revenue` (NUMERIC).\n\n```yaml\n# Schema layer (auto-generated)\ndimensions:\n  created_at: {}  # type: DATE, auto-generates timeframes\n  revenue: {}     # type: NUMERIC\n\n# Extension layer (your YAML)\ndimensions:\n  created_at:\n    label: \"Order Created\"\n    description: \"When the order was placed\"\n\n  revenue:\n    hidden: true  # Hide the raw column\n\nmeasures:\n  total_revenue:\n    sql: ${revenue}\n    aggregate_type: sum\n    format: currency_2\n```\n\nResult: `created_at` inherits its type from the schema layer (DATE with automatic week\u002Fmonth\u002Fyear granularities) but gets your label. The raw `revenue` column is hidden, only exposed through the `total_revenue` measure.\n\n**Key insight**: If your extension defines a dimension but there's no schema layer base dimension to provide type information, Omni can't infer granularities or types. Trigger a schema refresh to auto-generate the schema layer first.\n\n**Reading back what you wrote — `--mode`.** `yaml-get` returns your **extension** layer by default — just the deltas you authored, *not* the auto-generated base columns. To see the **fully-composed** result (schema base + your extension merged), read with `--mode combined`:\n\n```bash\n# What you authored (deltas only) — default\nomni models yaml-get \u003CmodelId> --filename your_view.view --branchid \u003CbranchId> --mode extension\n\n# What the model actually resolves to (schema + extension merged)\nomni models yaml-get \u003CmodelId> --filename your_view.view --branchid \u003CbranchId> --mode combined\n```\n\nUse `extension` to confirm *what you changed*, and `combined` to confirm *what the model resolves to*. When the model is git-integrated, the **combined** output mirrors what's written to the repository — which is why committed `*.view.yaml` files carry the schema-layer `table_name:` and base columns, while the extension layer holds only your deltas. (Other `--mode` values: `staged`, `merged`, `history`.)\n\n### Dimension Parameters\n\nSee `references\u002FmodelParameters.md` for the complete list of 35+ dimension parameters, format values, and timeframes.\n\nMost common parameters:\n- `sql` — SQL expression using `${field_name}` references. Reference other fields\n  with `${field}` \u002F `${view.field}`; a raw column auto-maps by name (no `sql:`).\n  There is no `${TABLE}` construct — `${TABLE}.column` errors with\n  `Column \"__omni_scoped\" not found` at validation and query time.\n- `label` — display name · `description` — help text (also used by Blobby)\n- `primary_key: true` — unique key (critical for aggregations)\n- `hidden: true` — hides from picker, still usable in SQL\n- `format` — `number_2`, `currency_2`, `percent_2`, `id`\n- `group_label` — groups fields in the picker\n- `synonyms` — alternative names for AI matching (e.g., `[client, account, buyer]`)\n\n### Measure Parameters\n\nSee `references\u002FmodelParameters.md` for the complete list of 24+ measure parameters and all 13 aggregate types.\n\nMeasure filters restrict rows before aggregation using the YAML filter condition syntax. See `references\u002Fyaml-filter-syntax.md` for the complete operator reference and measure filter examples.\n\nPrefer a measure `filters:` block for filtered aggregates instead of embedding filter logic in `sql` with `CASE WHEN` or a SQL `WHERE` clause. Keep `sql` focused on the value being aggregated:\n\n```yaml\nmeasures:\n  completed_revenue:\n    sql: ${sale_price}\n    aggregate_type: sum\n    filters:\n      status:\n        is: Complete\n```\n\n### Cross-View Fields in Views\n\nAvoid defining cross-view fields (dimensions or measures whose `sql` references `${other_view.field}`) directly in a view file. These fields depend on another view being joined, which is not guaranteed in every topic that includes this view. In topics where the referenced view isn't present, the field will be omitted — but more importantly, the model validator will throw errors for any topic that includes this view without also joining the referenced view. This can create a cascade of validator errors across topics that are otherwise valid but happen to include only a subset of the involved views.\n\n**In the vast majority of cases, cross-view fields should be defined in the topic's `views:` block** (see \"Topic-Scoped View Definitions\"), where the join context is explicit and controlled.\n\nOnly define a cross-view field in the view file itself when you are certain the referenced view will always be joined in every topic that includes this view — for example, when the join is defined globally and the two views are inseparable by design.\n\n## Fallback: View Missing from yaml-get\n\nBefore concluding that a view doesn't exist, always run this two-step check. `yaml-get` only returns views from currently-loaded schemas — views in offloaded or inactive schemas won't appear, but they're still available.\n\n```bash\n# 1. List all schemas the connection knows about (loaded, offloaded, and inactive)\nomni models get-schemas \u003CmodelId>\n# → {\"schemas\": [\"ANALYTICS\", \"PUBLIC\", \"STAGING\", ...]}\n\n# 2. If the target schema appears in the list, load it explicitly\nomni models yaml-get \u003CmodelId> --includeschemas PUBLIC\n```\n\n**Rules for `--includeschemas`:**\n- Accepts exactly **one schema name** per call — commas are rejected. Load schemas one at a time.\n- The response will contain only views from that schema; relationships to other schemas are preserved.\n- To scope to a branch, add `--branchid \u003Cid>` to `yaml-get` or `--branch-id \u003Cid>` to `get-schemas` (flag names differ per command).\n\nIf the schema isn't in the `get-schemas` list at all, the connection likely doesn't have access or the schema isn't synced — check with a Connection Admin.\n\n## Writing Topics\n\n> **Before writing a topic, verify all views you plan to reference actually exist.** Run `omni models yaml-get \u003CmodelId>` and confirm each view appears. If a view is missing, run the lazy-load fallback above before concluding it doesn't exist — it may simply be in an offloaded schema.\n\n### New topic vs extend an existing one\n\nWhen a query can't be answered by an existing topic, first check whether you should simply **extend** one rather than create a new one. Extending is usually right when the request's base view (the FROM) matches an existing topic's base view — e.g. add a relationship\u002Fjoin so a needed view becomes reachable, or add a field\u002Flabel. **Create a new topic** when any of these is fundamentally different:\n\n- **Subject \u002F object** — a different base view (FROM). What is the query fundamentally describing — orders? users? *time* (dates)? A different core entity warrants its own topic.\n- **Constraints** — conditions that are *always* applied (e.g. an `always_where` that excludes test users from order data). Different always-on filters → a different topic.\n- **Audience** — the same fields but different labels\u002Fterminology for a different consumer; jargon that differs by audience justifies a separate topic.\n\nPrompt the requestor when it's a judgment call, and build new topics on a branch (see the Safe Development Workflow above). Note that querying on a topic (vs a bare base view) is also what makes the result accessible to restricted queriers\u002Fviewers.\n\nSee [Topics setup](https:\u002F\u002Fdocs.omni.co\u002Fmodeling\u002Ftopics\u002Fsetup.md) for complete YAML examples with joins, fields, and ai_context, and [Topic parameters](https:\u002F\u002Fdocs.omni.co\u002Fmodeling\u002Ftopics\u002Fparameters.md) for all available options.\n\nKey topic elements:\n- `base_view` — the primary view for this topic\n- `joins` — nested structure for join chains (e.g., `users: {}` or `inventory_items: { products: {} }`)\n- `ai_context` — guides Blobby's field mapping (e.g., \"Map 'revenue' → total_revenue\")\n- `default_filters` — applied to all queries unless removed\n- `always_where_sql` — non-removable WHERE filter using a SQL expression (cannot be removed by users)\n- `always_where_filters` — non-removable WHERE filter using filter specifications (cannot be removed by users)\n- `always_having_sql` — non-removable HAVING filter using a SQL expression, applied after aggregation (cannot be removed by users)\n- `always_having_filters` — non-removable HAVING filter using filter specifications, applied after aggregation (cannot be removed by users)\n- `fields` — field curation: `[order_items.*, users.name, -users.internal_id]`\n\n### Filter Expressions for Topics\n\nWhen configuring `default_filters`, `always_where_filters`, or `always_having_filters` on a topic, use the YAML filter condition syntax — the same syntax used in measure filters. See `references\u002Fyaml-filter-syntax.md` for the complete reference.\n\nIf the right filter configuration for a given use case isn't obvious, use the Omni AI CLI to search the docs:\n\n```bash\nomni ai search-omni-docs \"how do I configure always_where_filters on a topic in Omni?\"\n```\n\nUse targeted questions to get precise YAML examples for your specific filtering need before writing the model YAML.\n\n## Writing Relationships\n\n### Global Relationships\n\nGlobal relationships are defined in the shared relationships file and are available across all topics. Use these for standard, reusable joins.\n\n```yaml\n- join_from_view: order_items\n  join_to_view: users\n  on_sql: ${order_items.user_id} = ${users.id}\n  relationship_type: many_to_one\n  join_type: always_left\n```\n\n| Type | When to Use |\n|------|-------------|\n| `many_to_one` | Orders → Users |\n| `one_to_many` | Users → Orders |\n| `one_to_one` | Users → User Settings |\n| `many_to_many` | Tags ↔ Products (rare) |\n\nGetting `relationship_type` right prevents fanout and symmetric aggregate errors.\n\n### Topic-Scoped Relationships\n\n> **Before defining, check the global relationships file** for a join between the same two views in either direction. Same `on_sql` → redundant, use `joins:` only. Different `on_sql` → default to the extended views pattern below rather than a silent override. Confirm intent with the modeler.\n\nUse topic-scoped relationships for one-off joins not in the shared model, or joining the same table multiple times under different conditions.\n\n```yaml\n# .topic file\nrelationships:\n  - join_from_view: order_items\n    join_to_view: users\n    on_sql: ${order_items.user_id} = ${users.id}\n    relationship_type: many_to_one\n    join_type: always_left\n\njoins:\n  users: {}\n```\n\n> **`joins` vs `relationships`:** `joins` declares which views are in the topic and their hierarchy; `relationships` defines the join conditions. A topic using only global relationships needs only `joins`. A topic with a one-off join needs both.\n\n#### Extended Views: Joining the Same Table Multiple Ways\n\nWhen the same table needs multiple joins (e.g., `users` as buyer and seller), use the **extended views** pattern — not `join_to_view_as`. Two variants:\n\n**Variant 1 — Global (reusable):** Create a standalone `.view` file with `extends:`, a role-descriptive name, and a `description:`. Define the relationship globally — any topic can then join it like any other view.\n\n**Variant 2 — Topic-scoped (inline):** Define the alias in the topic's `views:` block with its relationship in the same file. Use when the alias is not generally applicable in other topics.\n\nSee `references\u002Ftopic-scoped-relationships.md` for full YAML examples of both variants.\n\n> If you see a `relationship alias duplicates view name` error, this pattern is the fix.\n\n### Topic-Scoped View Definitions\n\nTopics can define or override views inline using a `views:` block — controlling `display_order`, overriding `label`, adding topic-specific filtered measures or derived dimensions, defining cross-view fields, and joining the same view multiple ways with per-alias conditions.\n\n> **Before adding any topic-scoped field to an existing view:**\n> 1. Read the view YAML (`omni models yaml-get`) and confirm the field doesn't already exist. If it does with the same definition, skip it.\n> 2. If a field with the same name exists but uses different SQL, this is an override. Confirm explicitly with the modeler — queries through this topic will use the topic-scoped definition; all other topics keep the shared one.\n\n```yaml\n# Example: display order + topic-specific filtered measure\nviews:\n  order_items:\n    display_order: 0\n    measures:\n      us_revenue:\n        sql: ${sale_price}\n        aggregate_type: sum\n        format: currency_2\n        filters:\n          users.country:\n            is: US\n```\n\nSee `references\u002Ftopic-scoped-views.md` for a full pattern gallery (label overrides, derived dimensions, cross-view fields, multi-join lifecycle, topic-scoped query views).\n\n> **Cross-view fields in `views:` blocks:** Before writing `${view_name.field_name}` references, confirm every referenced view is declared in the topic's `joins:` block — the model validator throws errors for any reference to a view that isn't joined.\n\n**Joining the same view multiple ways** (e.g., ARR at Start \u002F Current \u002F End): Use `extends:` inside the topic's `views:` block to create named aliases, each with its own `on_sql` in `relationships:`. Each alias inherits all base view fields and can override labels independently. For a full YAML example, see `references\u002Ftopic-scoped-views.md`.\n\n**Topic-scoped query views:** A query view can also be defined inside a topic's `views:` block, scoping it to that topic only. Same primary key rules apply (`primary_key: true` or `custom_compound_primary_key_sql`). Include a `relationships:` entry and a `joins:` entry for the new view — see Query Views section above, and `references\u002Ftopic-scoped-views.md` for a complete example.\n\n## Query Views\n\nVirtual tables defined by a saved query. A query view must have a primary key or it cannot be joined without producing fanout errors. **Before writing, confirm which field uniquely identifies each row — unless the primary key can be clearly inferred from the query itself and the involved views** (e.g. a query that selects `user_id` from a `users` view where `user_id` is the known primary key).\n\nThere are two ways to define the primary key:\n\n**Option 1 — Single unique field:** Mark exactly one dimension `primary_key: true` in the `dimensions:` block.\n\n**Option 2 — Compound key:** When no single field is unique but a combination is, set `custom_compound_primary_key_sql: [field_a, field_b]` at the view level — no `primary_key: true` dimension needed.\n\nBoth options work with either a `query:` block (field-mapped virtual table) or a `sql:` block (raw SELECT). In `sql:` blocks, use `${view_name}` to reference a view's underlying table rather than a hard-coded `CATALOG.SCHEMA.TABLE` path — it's preferred and stays correct if the table moves. See `references\u002Fquery-view-examples.md` for complete YAML for each variant.\n\n> If the user is unsure which field is unique, ask before writing the view. A query view without a primary key will trigger a \"Joins fan out the data without a primary key\" error when joined. See: https:\u002F\u002Fcommunity.omni.co\u002Ft\u002Fwhy-am-i-getting-the-error-joins-fan-out-the-data-without-a-primary-key\u002F37\n\nQuery views can also be defined inline within a topic's `views:` block, scoping the virtual table to that topic only. See `references\u002Ftopic-scoped-views.md` for an example.\n\n## Common Validation Errors\n\n| Error | Fix |\n|-------|-----|\n| \"No view X\" | Check view name spelling |\n| \"No join path from X to Y\" | Add a relationship |\n| \"Duplicate field name\" | Remove duplicate or rename (or suppress with `hidden: true` if one is auto-generated) |\n| \"Invalid YAML syntax\" | Check indentation (2 spaces, no tabs) |\n| Fanout \u002F incorrect aggregations on joins | Add `primary_key: true` to the joined view — every view that participates in a join must have a primary key |\n| Column reference error (e.g., \"Column `X` not found\") | Check that the table exists and your Omni connection has access |\n| Duplicate view appeared at the repo root after an edit | You wrote with a bare `fileName` instead of the file's full path. Delete the stray root file (send empty `yaml` to it) and re-write using the exact `files` key, including its folder prefix (e.g. `MARTS\u002F`) |\n\n## Troubleshooting: Model Out of Sync with Database\n\nIf the model doesn't reflect the database (missing columns\u002Ftables, wrong types, broken references), trigger a schema refresh (see \"Schema Refresh\" above), then `omni models validate \u003CmodelId>`. Field-name collisions and broken column references are usually fixed with `hidden: true` or a rename (see \"Common Validation Errors\"); persistent missing tables mean the connection lacks access to that database\u002Fschema.\n\n## Docs Reference\n\n- [Model YAML API](https:\u002F\u002Fdocs.omni.co\u002Fapi\u002Fmodels.md) · [Views](https:\u002F\u002Fdocs.omni.co\u002Fmodeling\u002Fviews.md) · [Topics](https:\u002F\u002Fdocs.omni.co\u002Fmodeling\u002Ftopics\u002Fparameters.md) · [Dimensions](https:\u002F\u002Fdocs.omni.co\u002Fmodeling\u002Fdimensions.md) · [Measures](https:\u002F\u002Fdocs.omni.co\u002Fmodeling\u002Fmeasures.md) · [Relationships](https:\u002F\u002Fdocs.omni.co\u002Fmodeling\u002Frelationships.md) · [Query Views](https:\u002F\u002Fdocs.omni.co\u002Fmodeling\u002Fquery-views.md) · [Branch Mode](https:\u002F\u002Fdocs.omni.co\u002Ffinding-content\u002Fdrafting-publishing\u002Fbranch-mode.md)\n\n## Related Skills\n\n- **omni-model-explorer** — understand the model before modifying\n- **omni-ai-optimizer** — add AI context after building topics\n- **omni-query** — test new fields\n",{"data":41,"body":42},{"name":4,"description":6},{"type":43,"children":44},"root",[45,53,59,83,90,178,294,387,421,427,439,484,501,507,512,591,612,618,652,665,671,703,709,787,793,798,805,865,886,919,961,1036,1042,1148,1175,1242,1283,1346,1352,1357,1553,1590,1596,1609,1660,1667,1672,1750,1794,1800,1856,1862,1903,2103,2154,2160,2255,2274,2280,2338,2462,2468,2686,2692,2711,2734,2752,2778,3052,3079,3089,3139,3306,3390,3396,3408,3413,3589,3595,3606,3619,3662,3763,3769,3789,3807,3812,3818,3830,3941,3956,4010,4022,4028,4049,4055,4074,4129,4134,4156,4161,4283,4289,4321,4326,4363,4368,4374,4380,4385,4482,4571,4584,4590,4626,4631,4776,4820,4826,4854,4887,4904,4916,4932,4938,4965,4997,5167,5179,5214,5259,5311,5317,5350,5355,5380,5405,5456,5470,5489,5495,5659,5665,5685,5691,5758,5764,5796],{"type":46,"tag":47,"props":48,"children":49},"element","h1",{"id":4},[50],{"type":51,"value":52},"text","Omni Model Builder",{"type":46,"tag":54,"props":55,"children":56},"p",{},[57],{"type":51,"value":58},"Create and modify Omni's semantic model through the YAML API — views, topics, dimensions, measures, relationships, and query views.",{"type":46,"tag":60,"props":61,"children":62},"blockquote",{},[63],{"type":46,"tag":54,"props":64,"children":65},{},[66,72,74,81],{"type":46,"tag":67,"props":68,"children":69},"strong",{},[70],{"type":51,"value":71},"Tip",{"type":51,"value":73},": Always use ",{"type":46,"tag":75,"props":76,"children":78},"code",{"className":77},[],[79],{"type":51,"value":80},"omni-model-explorer",{"type":51,"value":82}," first to understand the existing model.",{"type":46,"tag":84,"props":85,"children":87},"h2",{"id":86},"prerequisites",[88],{"type":51,"value":89},"Prerequisites",{"type":46,"tag":91,"props":92,"children":97},"pre",{"className":93,"code":94,"language":95,"meta":96,"style":96},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Verify the Omni CLI is installed — if not, ask the user to install it\n# See: https:\u002F\u002Fgithub.com\u002Fexploreomni\u002Fcli#readme\ncommand -v omni >\u002Fdev\u002Fnull || echo \"ERROR: Omni CLI is not installed.\"\n","bash","",[98],{"type":46,"tag":75,"props":99,"children":100},{"__ignoreMap":96},[101,113,122],{"type":46,"tag":102,"props":103,"children":106},"span",{"class":104,"line":105},"line",1,[107],{"type":46,"tag":102,"props":108,"children":110},{"style":109},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[111],{"type":51,"value":112},"# Verify the Omni CLI is installed — if not, ask the user to install it\n",{"type":46,"tag":102,"props":114,"children":116},{"class":104,"line":115},2,[117],{"type":46,"tag":102,"props":118,"children":119},{"style":109},[120],{"type":51,"value":121},"# See: https:\u002F\u002Fgithub.com\u002Fexploreomni\u002Fcli#readme\n",{"type":46,"tag":102,"props":123,"children":124},{"class":104,"line":33},[125,131,137,142,148,153,158,163,168,173],{"type":46,"tag":102,"props":126,"children":128},{"style":127},"--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF",[129],{"type":51,"value":130},"command",{"type":46,"tag":102,"props":132,"children":134},{"style":133},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[135],{"type":51,"value":136}," -v",{"type":46,"tag":102,"props":138,"children":139},{"style":133},[140],{"type":51,"value":141}," omni",{"type":46,"tag":102,"props":143,"children":145},{"style":144},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[146],{"type":51,"value":147}," >",{"type":46,"tag":102,"props":149,"children":150},{"style":133},[151],{"type":51,"value":152},"\u002Fdev\u002Fnull",{"type":46,"tag":102,"props":154,"children":155},{"style":144},[156],{"type":51,"value":157}," ||",{"type":46,"tag":102,"props":159,"children":160},{"style":127},[161],{"type":51,"value":162}," echo",{"type":46,"tag":102,"props":164,"children":165},{"style":144},[166],{"type":51,"value":167}," \"",{"type":46,"tag":102,"props":169,"children":170},{"style":133},[171],{"type":51,"value":172},"ERROR: Omni CLI is not installed.",{"type":46,"tag":102,"props":174,"children":175},{"style":144},[176],{"type":51,"value":177},"\"\n",{"type":46,"tag":91,"props":179,"children":181},{"className":93,"code":180,"language":95,"meta":96,"style":96},"# Show available profiles and select the appropriate one\nomni config show\n# If multiple profiles exist, ask the user which to use, then switch:\nomni config use \u003Cprofile-name>\n\n# Confirm the active profile is authenticated and inspect your permissions:\nomni whoami whoami\n",[182],{"type":46,"tag":75,"props":183,"children":184},{"__ignoreMap":96},[185,193,211,219,257,267,276],{"type":46,"tag":102,"props":186,"children":187},{"class":104,"line":105},[188],{"type":46,"tag":102,"props":189,"children":190},{"style":109},[191],{"type":51,"value":192},"# Show available profiles and select the appropriate one\n",{"type":46,"tag":102,"props":194,"children":195},{"class":104,"line":115},[196,201,206],{"type":46,"tag":102,"props":197,"children":199},{"style":198},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[200],{"type":51,"value":15},{"type":46,"tag":102,"props":202,"children":203},{"style":133},[204],{"type":51,"value":205}," config",{"type":46,"tag":102,"props":207,"children":208},{"style":133},[209],{"type":51,"value":210}," show\n",{"type":46,"tag":102,"props":212,"children":213},{"class":104,"line":33},[214],{"type":46,"tag":102,"props":215,"children":216},{"style":109},[217],{"type":51,"value":218},"# If multiple profiles exist, ask the user which to use, then switch:\n",{"type":46,"tag":102,"props":220,"children":222},{"class":104,"line":221},4,[223,227,231,236,241,246,252],{"type":46,"tag":102,"props":224,"children":225},{"style":198},[226],{"type":51,"value":15},{"type":46,"tag":102,"props":228,"children":229},{"style":133},[230],{"type":51,"value":205},{"type":46,"tag":102,"props":232,"children":233},{"style":133},[234],{"type":51,"value":235}," use",{"type":46,"tag":102,"props":237,"children":238},{"style":144},[239],{"type":51,"value":240}," \u003C",{"type":46,"tag":102,"props":242,"children":243},{"style":133},[244],{"type":51,"value":245},"profile-nam",{"type":46,"tag":102,"props":247,"children":249},{"style":248},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[250],{"type":51,"value":251},"e",{"type":46,"tag":102,"props":253,"children":254},{"style":144},[255],{"type":51,"value":256},">\n",{"type":46,"tag":102,"props":258,"children":260},{"class":104,"line":259},5,[261],{"type":46,"tag":102,"props":262,"children":264},{"emptyLinePlaceholder":263},true,[265],{"type":51,"value":266},"\n",{"type":46,"tag":102,"props":268,"children":270},{"class":104,"line":269},6,[271],{"type":46,"tag":102,"props":272,"children":273},{"style":109},[274],{"type":51,"value":275},"# Confirm the active profile is authenticated and inspect your permissions:\n",{"type":46,"tag":102,"props":277,"children":279},{"class":104,"line":278},7,[280,284,289],{"type":46,"tag":102,"props":281,"children":282},{"style":198},[283],{"type":51,"value":15},{"type":46,"tag":102,"props":285,"children":286},{"style":133},[287],{"type":51,"value":288}," whoami",{"type":46,"tag":102,"props":290,"children":291},{"style":133},[292],{"type":51,"value":293}," whoami\n",{"type":46,"tag":60,"props":295,"children":296},{},[297],{"type":46,"tag":54,"props":298,"children":299},{},[300,305,307,312,314,319,321,327,329,334,336,342,344,350,352,358,360,369,371,377,379,385],{"type":46,"tag":67,"props":301,"children":302},{},[303],{"type":51,"value":304},"Auth",{"type":51,"value":306},": a profile authenticates with an ",{"type":46,"tag":67,"props":308,"children":309},{},[310],{"type":51,"value":311},"API key",{"type":51,"value":313}," or ",{"type":46,"tag":67,"props":315,"children":316},{},[317],{"type":51,"value":318},"OAuth",{"type":51,"value":320},". If ",{"type":46,"tag":75,"props":322,"children":324},{"className":323},[],[325],{"type":51,"value":326},"whoami",{"type":51,"value":328}," (or any call) returns ",{"type":46,"tag":67,"props":330,"children":331},{},[332],{"type":51,"value":333},"401",{"type":51,"value":335},", hand off — ask the user to run ",{"type":46,"tag":75,"props":337,"children":339},{"className":338},[],[340],{"type":51,"value":341},"! omni config login \u003Cprofile>",{"type":51,"value":343}," (OAuth 2.1 browser flow; it blocks ~2 min on the browser). Don't run ",{"type":46,"tag":75,"props":345,"children":347},{"className":346},[],[348],{"type":51,"value":349},"config login",{"type":51,"value":351}," yourself in a headless\u002FCI session (no browser → timeout); on a local interactive machine you ",{"type":46,"tag":353,"props":354,"children":355},"em",{},[356],{"type":51,"value":357},"may",{"type":51,"value":359},". See the ",{"type":46,"tag":67,"props":361,"children":362},{},[363],{"type":46,"tag":75,"props":364,"children":366},{"className":365},[],[367],{"type":51,"value":368},"omni-api-conventions",{"type":51,"value":370}," rule for profile setup (",{"type":46,"tag":75,"props":372,"children":374},{"className":373},[],[375],{"type":51,"value":376},"omni config init --auth oauth",{"type":51,"value":378},") and discovering request-body shapes with ",{"type":46,"tag":75,"props":380,"children":382},{"className":381},[],[383],{"type":51,"value":384},"--schema",{"type":51,"value":386},".",{"type":46,"tag":54,"props":388,"children":389},{},[390,392,397,398,403,405,411,413,419],{"type":51,"value":391},"You need ",{"type":46,"tag":67,"props":393,"children":394},{},[395],{"type":51,"value":396},"Modeler",{"type":51,"value":313},{"type":46,"tag":67,"props":399,"children":400},{},[401],{"type":51,"value":402},"Connection Admin",{"type":51,"value":404}," permissions. Add ",{"type":46,"tag":75,"props":406,"children":408},{"className":407},[],[409],{"type":51,"value":410},"-o json",{"type":51,"value":412}," to any command to force structured output for parsing (default ",{"type":46,"tag":75,"props":414,"children":416},{"className":415},[],[417],{"type":51,"value":418},"auto",{"type":51,"value":420}," is human in a TTY, JSON when piped).",{"type":46,"tag":84,"props":422,"children":424},{"id":423},"omnis-layered-modeling-architecture",[425],{"type":51,"value":426},"Omni's Layered Modeling Architecture",{"type":46,"tag":54,"props":428,"children":429},{},[430,432,437],{"type":51,"value":431},"Omni uses a ",{"type":46,"tag":67,"props":433,"children":434},{},[435],{"type":51,"value":436},"layered approach",{"type":51,"value":438}," where each layer builds on top of the previous:",{"type":46,"tag":440,"props":441,"children":442},"ol",{},[443,454,464,474],{"type":46,"tag":444,"props":445,"children":446},"li",{},[447,452],{"type":46,"tag":67,"props":448,"children":449},{},[450],{"type":51,"value":451},"Schema Layer",{"type":51,"value":453}," — Auto-generated from your database. Reflects tables, views, columns, and their types. Kept in sync via schema refresh.",{"type":46,"tag":444,"props":455,"children":456},{},[457,462],{"type":46,"tag":67,"props":458,"children":459},{},[460],{"type":51,"value":461},"Shared Model Layer",{"type":51,"value":463}," — Your governed semantic model. Where you define dimensions, measures, joins, and topics that are reusable across the organization.",{"type":46,"tag":444,"props":465,"children":466},{},[467,472],{"type":46,"tag":67,"props":468,"children":469},{},[470],{"type":51,"value":471},"Workbook Model Layer",{"type":51,"value":473}," — Ad hoc extensions within individual workbooks. Used for experimental fields before promotion to shared model.",{"type":46,"tag":444,"props":475,"children":476},{},[477,482],{"type":46,"tag":67,"props":478,"children":479},{},[480],{"type":51,"value":481},"Branch Layer",{"type":51,"value":483}," — Intermediate development layer. Used when working in branches before merging changes to shared model.",{"type":46,"tag":54,"props":485,"children":486},{},[487,492,494,499],{"type":46,"tag":67,"props":488,"children":489},{},[490],{"type":51,"value":491},"Key concept",{"type":51,"value":493},": The schema layer is the source of truth for table\u002Fcolumn structure (refreshed when the database changes); all user-created content (dimensions, measures, relationships, topics) flows through the shared model layer. You build and modify it in ",{"type":46,"tag":67,"props":495,"children":496},{},[497],{"type":51,"value":498},"branches",{"type":51,"value":500}," (see \"Safe Development Workflow\" below) before merging back to the shared model.",{"type":46,"tag":84,"props":502,"children":504},{"id":503},"determine-sql-dialect",[505],{"type":51,"value":506},"Determine SQL Dialect",{"type":46,"tag":54,"props":508,"children":509},{},[510],{"type":51,"value":511},"Before writing any SQL expressions, confirm the dialect from the connection — don't guess from the connection name:",{"type":46,"tag":91,"props":513,"children":515},{"className":93,"code":514,"language":95,"meta":96,"style":96},"# 1. List models to find connectionId\nomni models list\n\n# 2. Look up the connection's dialect\nomni connections list\n# → find your connectionId and read the \"dialect\" field\n# → e.g. \"bigquery\", \"postgres\", \"snowflake\", \"databricks\"\n",[516],{"type":46,"tag":75,"props":517,"children":518},{"__ignoreMap":96},[519,527,544,551,559,575,583],{"type":46,"tag":102,"props":520,"children":521},{"class":104,"line":105},[522],{"type":46,"tag":102,"props":523,"children":524},{"style":109},[525],{"type":51,"value":526},"# 1. List models to find connectionId\n",{"type":46,"tag":102,"props":528,"children":529},{"class":104,"line":115},[530,534,539],{"type":46,"tag":102,"props":531,"children":532},{"style":198},[533],{"type":51,"value":15},{"type":46,"tag":102,"props":535,"children":536},{"style":133},[537],{"type":51,"value":538}," models",{"type":46,"tag":102,"props":540,"children":541},{"style":133},[542],{"type":51,"value":543}," list\n",{"type":46,"tag":102,"props":545,"children":546},{"class":104,"line":33},[547],{"type":46,"tag":102,"props":548,"children":549},{"emptyLinePlaceholder":263},[550],{"type":51,"value":266},{"type":46,"tag":102,"props":552,"children":553},{"class":104,"line":221},[554],{"type":46,"tag":102,"props":555,"children":556},{"style":109},[557],{"type":51,"value":558},"# 2. Look up the connection's dialect\n",{"type":46,"tag":102,"props":560,"children":561},{"class":104,"line":259},[562,566,571],{"type":46,"tag":102,"props":563,"children":564},{"style":198},[565],{"type":51,"value":15},{"type":46,"tag":102,"props":567,"children":568},{"style":133},[569],{"type":51,"value":570}," connections",{"type":46,"tag":102,"props":572,"children":573},{"style":133},[574],{"type":51,"value":543},{"type":46,"tag":102,"props":576,"children":577},{"class":104,"line":269},[578],{"type":46,"tag":102,"props":579,"children":580},{"style":109},[581],{"type":51,"value":582},"# → find your connectionId and read the \"dialect\" field\n",{"type":46,"tag":102,"props":584,"children":585},{"class":104,"line":278},[586],{"type":46,"tag":102,"props":587,"children":588},{"style":109},[589],{"type":51,"value":590},"# → e.g. \"bigquery\", \"postgres\", \"snowflake\", \"databricks\"\n",{"type":46,"tag":54,"props":592,"children":593},{},[594,596,602,604,610],{"type":51,"value":595},"Use dialect-appropriate functions in your SQL (e.g. ",{"type":46,"tag":75,"props":597,"children":599},{"className":598},[],[600],{"type":51,"value":601},"SAFE_DIVIDE",{"type":51,"value":603}," for BigQuery, ",{"type":46,"tag":75,"props":605,"children":607},{"className":606},[],[608],{"type":51,"value":609},"NULLIF(a\u002Fb)",{"type":51,"value":611}," for Postgres\u002FSnowflake).",{"type":46,"tag":84,"props":613,"children":615},{"id":614},"schema-refresh-syncing-with-database-changes",[616],{"type":51,"value":617},"Schema Refresh: Syncing with Database Changes",{"type":46,"tag":54,"props":619,"children":620},{},[621,623,628,630,636,638,644,646,650],{"type":51,"value":622},"The ",{"type":46,"tag":67,"props":624,"children":625},{},[626],{"type":51,"value":627},"schema layer",{"type":51,"value":629}," is auto-generated from your database. When your database schema changes (new\u002Fdeleted\u002Frenamed columns, type changes), refresh it to stay in sync: ",{"type":46,"tag":75,"props":631,"children":633},{"className":632},[],[634],{"type":51,"value":635},"omni models refresh \u003CmodelId>",{"type":51,"value":637}," (add ",{"type":46,"tag":75,"props":639,"children":641},{"className":640},[],[642],{"type":51,"value":643},"--branch-id \u003CbranchId>",{"type":51,"value":645}," to scope to a branch; requires ",{"type":46,"tag":67,"props":647,"children":648},{},[649],{"type":51,"value":402},{"type":51,"value":651},").",{"type":46,"tag":54,"props":653,"children":654},{},[655,657,663],{"type":51,"value":656},"See ",{"type":46,"tag":75,"props":658,"children":660},{"className":659},[],[661],{"type":51,"value":662},"references\u002Fschema-refresh.md",{"type":51,"value":664}," for when to trigger, what it does and its side effects, the deleted\u002Frenamed-column impact-check workflow, and connection\u002Fcredential error handling.",{"type":46,"tag":84,"props":666,"children":668},{"id":667},"known-issues-safe-defaults",[669],{"type":51,"value":670},"Known Issues & Safe Defaults",{"type":46,"tag":672,"props":673,"children":674},"ul",{},[675,693],{"type":46,"tag":444,"props":676,"children":677},{},[678,683,685,691],{"type":46,"tag":67,"props":679,"children":680},{},[681],{"type":51,"value":682},"Do not merge without explicit confirmation",{"type":51,"value":684}," — after branch validation and query testing, stop and ask the user before ",{"type":46,"tag":75,"props":686,"children":688},{"className":687},[],[689],{"type":51,"value":690},"omni models merge-branch",{"type":51,"value":692},", even when the model is not git-connected. Treat requests like \"add a field\" or \"create a view\" as requests to prepare validated branch changes, not as permission to ship to production.",{"type":46,"tag":444,"props":694,"children":695},{},[696,701],{"type":46,"tag":67,"props":697,"children":698},{},[699],{"type":51,"value":700},"Keep eval-created files on branches until confirmed",{"type":51,"value":702}," — if you create fields\u002Fviews for validation, report the branch name\u002Fid, validation status, and test query result. Only merge after the user explicitly says to merge, ship, publish, or promote.",{"type":46,"tag":84,"props":704,"children":706},{"id":705},"discovering-commands",[707],{"type":51,"value":708},"Discovering Commands",{"type":46,"tag":91,"props":710,"children":712},{"className":93,"code":711,"language":95,"meta":96,"style":96},"omni models --help                # List all model operations\nomni models yaml-create --help    # Show flags for writing YAML\nomni models yaml-create --schema  # Print the body's JSON schema + a filled example (no token)\n",[713],{"type":46,"tag":75,"props":714,"children":715},{"__ignoreMap":96},[716,737,762],{"type":46,"tag":102,"props":717,"children":718},{"class":104,"line":105},[719,723,727,732],{"type":46,"tag":102,"props":720,"children":721},{"style":198},[722],{"type":51,"value":15},{"type":46,"tag":102,"props":724,"children":725},{"style":133},[726],{"type":51,"value":538},{"type":46,"tag":102,"props":728,"children":729},{"style":133},[730],{"type":51,"value":731}," --help",{"type":46,"tag":102,"props":733,"children":734},{"style":109},[735],{"type":51,"value":736},"                # List all model operations\n",{"type":46,"tag":102,"props":738,"children":739},{"class":104,"line":115},[740,744,748,753,757],{"type":46,"tag":102,"props":741,"children":742},{"style":198},[743],{"type":51,"value":15},{"type":46,"tag":102,"props":745,"children":746},{"style":133},[747],{"type":51,"value":538},{"type":46,"tag":102,"props":749,"children":750},{"style":133},[751],{"type":51,"value":752}," yaml-create",{"type":46,"tag":102,"props":754,"children":755},{"style":133},[756],{"type":51,"value":731},{"type":46,"tag":102,"props":758,"children":759},{"style":109},[760],{"type":51,"value":761},"    # Show flags for writing YAML\n",{"type":46,"tag":102,"props":763,"children":764},{"class":104,"line":33},[765,769,773,777,782],{"type":46,"tag":102,"props":766,"children":767},{"style":198},[768],{"type":51,"value":15},{"type":46,"tag":102,"props":770,"children":771},{"style":133},[772],{"type":51,"value":538},{"type":46,"tag":102,"props":774,"children":775},{"style":133},[776],{"type":51,"value":752},{"type":46,"tag":102,"props":778,"children":779},{"style":133},[780],{"type":51,"value":781}," --schema",{"type":46,"tag":102,"props":783,"children":784},{"style":109},[785],{"type":51,"value":786},"  # Print the body's JSON schema + a filled example (no token)\n",{"type":46,"tag":84,"props":788,"children":790},{"id":789},"safe-development-workflow",[791],{"type":51,"value":792},"Safe Development Workflow",{"type":46,"tag":54,"props":794,"children":795},{},[796],{"type":51,"value":797},"Always work in a branch. Never write directly to production.",{"type":46,"tag":799,"props":800,"children":802},"h3",{"id":801},"step-0-create-a-branch",[803],{"type":51,"value":804},"Step 0: Create a Branch",{"type":46,"tag":91,"props":806,"children":808},{"className":93,"code":807,"language":95,"meta":96,"style":96},"omni models create-branch \u003CmodelId> --name \"my-feature-branch\"\n",[809],{"type":46,"tag":75,"props":810,"children":811},{"__ignoreMap":96},[812],{"type":46,"tag":102,"props":813,"children":814},{"class":104,"line":105},[815,819,823,828,832,837,842,847,852,856,861],{"type":46,"tag":102,"props":816,"children":817},{"style":198},[818],{"type":51,"value":15},{"type":46,"tag":102,"props":820,"children":821},{"style":133},[822],{"type":51,"value":538},{"type":46,"tag":102,"props":824,"children":825},{"style":133},[826],{"type":51,"value":827}," create-branch",{"type":46,"tag":102,"props":829,"children":830},{"style":144},[831],{"type":51,"value":240},{"type":46,"tag":102,"props":833,"children":834},{"style":133},[835],{"type":51,"value":836},"modelI",{"type":46,"tag":102,"props":838,"children":839},{"style":248},[840],{"type":51,"value":841},"d",{"type":46,"tag":102,"props":843,"children":844},{"style":144},[845],{"type":51,"value":846},">",{"type":46,"tag":102,"props":848,"children":849},{"style":133},[850],{"type":51,"value":851}," --name",{"type":46,"tag":102,"props":853,"children":854},{"style":144},[855],{"type":51,"value":167},{"type":46,"tag":102,"props":857,"children":858},{"style":133},[859],{"type":51,"value":860},"my-feature-branch",{"type":46,"tag":102,"props":862,"children":863},{"style":144},[864],{"type":51,"value":177},{"type":46,"tag":54,"props":866,"children":867},{},[868,870,876,878,884],{"type":51,"value":869},"The response ",{"type":46,"tag":75,"props":871,"children":873},{"className":872},[],[874],{"type":51,"value":875},"model.id",{"type":51,"value":877}," is your ",{"type":46,"tag":75,"props":879,"children":881},{"className":880},[],[882],{"type":51,"value":883},"branchId",{"type":51,"value":885}," — a UUID you'll pass to all subsequent API calls. To list existing branches at any time:",{"type":46,"tag":91,"props":887,"children":889},{"className":93,"code":888,"language":95,"meta":96,"style":96},"omni models list --include activeBranches\n",[890],{"type":46,"tag":75,"props":891,"children":892},{"__ignoreMap":96},[893],{"type":46,"tag":102,"props":894,"children":895},{"class":104,"line":105},[896,900,904,909,914],{"type":46,"tag":102,"props":897,"children":898},{"style":198},[899],{"type":51,"value":15},{"type":46,"tag":102,"props":901,"children":902},{"style":133},[903],{"type":51,"value":538},{"type":46,"tag":102,"props":905,"children":906},{"style":133},[907],{"type":51,"value":908}," list",{"type":46,"tag":102,"props":910,"children":911},{"style":133},[912],{"type":51,"value":913}," --include",{"type":46,"tag":102,"props":915,"children":916},{"style":133},[917],{"type":51,"value":918}," activeBranches\n",{"type":46,"tag":60,"props":920,"children":921},{},[922],{"type":46,"tag":54,"props":923,"children":924},{},[925,930,932,937,939,944,946,952,954,959],{"type":46,"tag":67,"props":926,"children":927},{},[928],{"type":51,"value":929},"Git-connected models",{"type":51,"value":931},": The repo is a ",{"type":46,"tag":353,"props":933,"children":934},{},[935],{"type":51,"value":936},"projection",{"type":51,"value":938}," of Omni's model for governance (pull requests, review, audit trail) — not the source of truth, and not a surface to author against. Omni's model state is authoritative, so model YAML hand-edited in the repo is validated only after you push, re-serialized to Omni's canonical form, and overwritten on the next regeneration. Author ",{"type":46,"tag":67,"props":940,"children":941},{},[942],{"type":51,"value":943},"through the Omni APIs on a branch",{"type":51,"value":945}," (via the Omni CLI, as below), then use ",{"type":46,"tag":75,"props":947,"children":949},{"className":948},[],[950],{"type":51,"value":951},"omni models commit",{"type":51,"value":953}," (Step 3 below) to sync the branch to git as a pull request and ",{"type":46,"tag":67,"props":955,"children":956},{},[957],{"type":51,"value":958},"review\u002Fmerge in your git provider",{"type":51,"value":960}," — don't hand-edit model YAML in the repo and push directly.",{"type":46,"tag":60,"props":962,"children":963},{},[964,988,998],{"type":46,"tag":54,"props":965,"children":966},{},[967,972,974,979,981,986],{"type":46,"tag":67,"props":968,"children":969},{},[970],{"type":51,"value":971},"⚠️ Never hand-edit model YAML in git for a git-connected shared model.",{"type":51,"value":973}," It can look correct in git for a while, but Omni regenerates the default branch from its own authoritative model state and ",{"type":46,"tag":67,"props":975,"children":976},{},[977],{"type":51,"value":978},"deletes git-only model files",{"type":51,"value":980}," that state doesn't contain — an observed customer incident, where model files committed only to git silently disappeared on Omni's next sync. All view \u002F topic \u002F relationship YAML must originate in Omni: edit on a branch → ",{"type":46,"tag":75,"props":982,"children":984},{"className":983},[],[985],{"type":51,"value":951},{"type":51,"value":987}," → PR.",{"type":46,"tag":54,"props":989,"children":990},{},[991,996],{"type":46,"tag":67,"props":992,"children":993},{},[994],{"type":51,"value":995},"Model content vs. repo governance",{"type":51,"value":997}," — the split that makes this safe:",{"type":46,"tag":672,"props":999,"children":1000},{},[1001,1018],{"type":46,"tag":444,"props":1002,"children":1003},{},[1004,1009,1011,1016],{"type":46,"tag":67,"props":1005,"children":1006},{},[1007],{"type":51,"value":1008},"Omni model content",{"type":51,"value":1010}," (view \u002F topic \u002F relationship YAML): author on an Omni branch → ",{"type":46,"tag":75,"props":1012,"children":1014},{"className":1013},[],[1015],{"type":51,"value":951},{"type":51,"value":1017}," → PR. Never commit it directly in git — Omni will regenerate over it.",{"type":46,"tag":444,"props":1019,"children":1020},{},[1021,1026,1028,1034],{"type":46,"tag":67,"props":1022,"children":1023},{},[1024],{"type":51,"value":1025},"Repo-governance \u002F non-model files",{"type":51,"value":1027}," (a root ",{"type":46,"tag":75,"props":1029,"children":1031},{"className":1030},[],[1032],{"type":51,"value":1033},"omni\u002FOWNERS.yaml",{"type":51,"value":1035},", CODEOWNERS, CI config, docs, scripts): not Omni model content, so direct git commits are fine — Omni's regeneration leaves them untouched.",{"type":46,"tag":799,"props":1037,"children":1039},{"id":1038},"step-1-write-yaml-to-a-branch",[1040],{"type":51,"value":1041},"Step 1: Write YAML to a Branch",{"type":46,"tag":91,"props":1043,"children":1045},{"className":93,"code":1044,"language":95,"meta":96,"style":96},"omni models yaml-create \u003CmodelId> --body '{\n  \"fileName\": \"my_new_view.view\",\n  \"yaml\": \"dimensions:\\n  order_id:\\n    primary_key: true\\n  status:\\n    label: Order Status\\nmeasures:\\n  count:\\n    aggregate_type: count\",\n  \"mode\": \"extension\",\n  \"branchId\": \"{branchId}\",\n  \"commitMessage\": \"Add my_new_view with status dimension and count measure\"\n}'\n",[1046],{"type":46,"tag":75,"props":1047,"children":1048},{"__ignoreMap":96},[1049,1095,1103,1111,1119,1127,1135],{"type":46,"tag":102,"props":1050,"children":1051},{"class":104,"line":105},[1052,1056,1060,1064,1068,1072,1076,1080,1085,1090],{"type":46,"tag":102,"props":1053,"children":1054},{"style":198},[1055],{"type":51,"value":15},{"type":46,"tag":102,"props":1057,"children":1058},{"style":133},[1059],{"type":51,"value":538},{"type":46,"tag":102,"props":1061,"children":1062},{"style":133},[1063],{"type":51,"value":752},{"type":46,"tag":102,"props":1065,"children":1066},{"style":144},[1067],{"type":51,"value":240},{"type":46,"tag":102,"props":1069,"children":1070},{"style":133},[1071],{"type":51,"value":836},{"type":46,"tag":102,"props":1073,"children":1074},{"style":248},[1075],{"type":51,"value":841},{"type":46,"tag":102,"props":1077,"children":1078},{"style":144},[1079],{"type":51,"value":846},{"type":46,"tag":102,"props":1081,"children":1082},{"style":133},[1083],{"type":51,"value":1084}," --body",{"type":46,"tag":102,"props":1086,"children":1087},{"style":144},[1088],{"type":51,"value":1089}," '",{"type":46,"tag":102,"props":1091,"children":1092},{"style":133},[1093],{"type":51,"value":1094},"{\n",{"type":46,"tag":102,"props":1096,"children":1097},{"class":104,"line":115},[1098],{"type":46,"tag":102,"props":1099,"children":1100},{"style":133},[1101],{"type":51,"value":1102},"  \"fileName\": \"my_new_view.view\",\n",{"type":46,"tag":102,"props":1104,"children":1105},{"class":104,"line":33},[1106],{"type":46,"tag":102,"props":1107,"children":1108},{"style":133},[1109],{"type":51,"value":1110},"  \"yaml\": \"dimensions:\\n  order_id:\\n    primary_key: true\\n  status:\\n    label: Order Status\\nmeasures:\\n  count:\\n    aggregate_type: count\",\n",{"type":46,"tag":102,"props":1112,"children":1113},{"class":104,"line":221},[1114],{"type":46,"tag":102,"props":1115,"children":1116},{"style":133},[1117],{"type":51,"value":1118},"  \"mode\": \"extension\",\n",{"type":46,"tag":102,"props":1120,"children":1121},{"class":104,"line":259},[1122],{"type":46,"tag":102,"props":1123,"children":1124},{"style":133},[1125],{"type":51,"value":1126},"  \"branchId\": \"{branchId}\",\n",{"type":46,"tag":102,"props":1128,"children":1129},{"class":104,"line":269},[1130],{"type":46,"tag":102,"props":1131,"children":1132},{"style":133},[1133],{"type":51,"value":1134},"  \"commitMessage\": \"Add my_new_view with status dimension and count measure\"\n",{"type":46,"tag":102,"props":1136,"children":1137},{"class":104,"line":278},[1138,1143],{"type":46,"tag":102,"props":1139,"children":1140},{"style":133},[1141],{"type":51,"value":1142},"}",{"type":46,"tag":102,"props":1144,"children":1145},{"style":144},[1146],{"type":51,"value":1147},"'\n",{"type":46,"tag":60,"props":1149,"children":1150},{},[1151],{"type":46,"tag":54,"props":1152,"children":1153},{},[1154,1159,1161,1166,1168,1174],{"type":46,"tag":67,"props":1155,"children":1156},{},[1157],{"type":51,"value":1158},"Note",{"type":51,"value":1160},": The ",{"type":46,"tag":75,"props":1162,"children":1164},{"className":1163},[],[1165],{"type":51,"value":883},{"type":51,"value":1167}," parameter must be a UUID from the server (Step 0). Passing a string name instead will return ",{"type":46,"tag":75,"props":1169,"children":1171},{"className":1170},[],[1172],{"type":51,"value":1173},"400 Bad Request: Unrecognized key: \"branchName\"",{"type":51,"value":386},{"type":46,"tag":60,"props":1176,"children":1177},{},[1178],{"type":46,"tag":54,"props":1179,"children":1180},{},[1181,1194,1196,1202,1204,1210,1212,1218,1220,1225,1227,1232,1234,1240],{"type":46,"tag":67,"props":1182,"children":1183},{},[1184,1186,1192],{"type":51,"value":1185},"⚠️ Editing an existing file? ",{"type":46,"tag":75,"props":1187,"children":1189},{"className":1188},[],[1190],{"type":51,"value":1191},"fileName",{"type":51,"value":1193}," is its exact path, not a regex (unlike on read).",{"type":51,"value":1195}," Reuse the full-path key from your ",{"type":46,"tag":75,"props":1197,"children":1199},{"className":1198},[],[1200],{"type":51,"value":1201},"yaml-get",{"type":51,"value":1203}," response verbatim, including any folder prefix — e.g. ",{"type":46,"tag":75,"props":1205,"children":1207},{"className":1206},[],[1208],{"type":51,"value":1209},"MARTS\u002Ffct_ai_events.view",{"type":51,"value":1211},", not ",{"type":46,"tag":75,"props":1213,"children":1215},{"className":1214},[],[1216],{"type":51,"value":1217},"fct_ai_events.view",{"type":51,"value":1219},". A non-matching ",{"type":46,"tag":75,"props":1221,"children":1223},{"className":1222},[],[1224],{"type":51,"value":1191},{"type":51,"value":1226}," doesn't error: Omni ",{"type":46,"tag":67,"props":1228,"children":1229},{},[1230],{"type":51,"value":1231},"silently creates a new file at that path",{"type":51,"value":1233}," and returns ",{"type":46,"tag":75,"props":1235,"children":1237},{"className":1236},[],[1238],{"type":51,"value":1239},"success: true",{"type":51,"value":1241},", so shortening the key produces a duplicate view at the repo root.",{"type":46,"tag":60,"props":1243,"children":1244},{},[1245],{"type":46,"tag":54,"props":1246,"children":1247},{},[1248,1253,1255,1261,1262,1267,1269,1274,1276,1281],{"type":46,"tag":67,"props":1249,"children":1250},{},[1251],{"type":51,"value":1252},"Edits are whole-file writes — read-modify-write.",{"type":51,"value":1254}," ",{"type":46,"tag":75,"props":1256,"children":1258},{"className":1257},[],[1259],{"type":51,"value":1260},"yaml-create",{"type":51,"value":1254},{"type":46,"tag":67,"props":1263,"children":1264},{},[1265],{"type":51,"value":1266},"replaces",{"type":51,"value":1268}," a file's authored content; it does not merge field-by-field. To change or add one field on an existing view, ",{"type":46,"tag":75,"props":1270,"children":1272},{"className":1271},[],[1273],{"type":51,"value":1201},{"type":51,"value":1275}," the file first, edit it, and write the ",{"type":46,"tag":67,"props":1277,"children":1278},{},[1279],{"type":51,"value":1280},"complete",{"type":51,"value":1282}," file back — otherwise the other authored fields are dropped. (Schema base columns are unaffected — they live in the schema layer, not the authored file.)",{"type":46,"tag":60,"props":1284,"children":1285},{},[1286],{"type":46,"tag":54,"props":1287,"children":1288},{},[1289,1294,1295,1301,1302,1307,1308,1314,1316,1322,1324,1329,1331,1337,1339,1344],{"type":46,"tag":67,"props":1290,"children":1291},{},[1292],{"type":51,"value":1293},"Inspect a branch.",{"type":51,"value":1254},{"type":46,"tag":75,"props":1296,"children":1298},{"className":1297},[],[1299],{"type":51,"value":1300},"yaml-get \u003CmodelId> --branchid \u003CbranchId>",{"type":51,"value":1254},{"type":46,"tag":67,"props":1303,"children":1304},{},[1305],{"type":51,"value":1306},"without",{"type":51,"value":1254},{"type":46,"tag":75,"props":1309,"children":1311},{"className":1310},[],[1312],{"type":51,"value":1313},"--filename",{"type":51,"value":1315}," enumerates the whole model — ",{"type":46,"tag":75,"props":1317,"children":1319},{"className":1318},[],[1320],{"type":51,"value":1321},"--mode extension",{"type":51,"value":1323}," returns only the files the branch ",{"type":46,"tag":67,"props":1325,"children":1326},{},[1327],{"type":51,"value":1328},"changed",{"type":51,"value":1330}," (your deltas); ",{"type":46,"tag":75,"props":1332,"children":1334},{"className":1333},[],[1335],{"type":51,"value":1336},"--mode combined",{"type":51,"value":1338}," returns the ",{"type":46,"tag":67,"props":1340,"children":1341},{},[1342],{"type":51,"value":1343},"full composed",{"type":51,"value":1345}," model (schema + shared + branch). Then drill into any file by its exact path.",{"type":46,"tag":799,"props":1347,"children":1349},{"id":1348},"step-2-validate-and-test",[1350],{"type":51,"value":1351},"Step 2: Validate and Test",{"type":46,"tag":54,"props":1353,"children":1354},{},[1355],{"type":51,"value":1356},"Every YAML write must be validated and tested before merging — a field can be valid YAML yet produce wrong results or broken queries.",{"type":46,"tag":91,"props":1358,"children":1360},{"className":93,"code":1359,"language":95,"meta":96,"style":96},"# 1. Validate — any issue with is_warning:false is a blocking error; fix before proceeding\nomni models validate \u003CmodelId> --branchid \u003CbranchId>\n\n# 2. Query the fields you changed — confirm no error and summary.row_count > 0\nomni query run --body '{\"query\":{\"modelId\":\"\u003CmodelId>\",\"table\":\"your_view\",\"fields\":[\"your_view.new_dimension\",\"your_view.new_measure\"],\"limit\":10,\"join_paths_from_topic_name\":\"your_topic\"},\"branchId\":\"\u003CbranchId>\"}'\n\n# 3. Read it back — confirm the field is present (and not duplicated at a second path)\nomni models yaml-get \u003CmodelId> --filename your_view.view --branchid \u003CbranchId>\n",[1361],{"type":46,"tag":75,"props":1362,"children":1363},{"__ignoreMap":96},[1364,1372,1426,1433,1441,1475,1482,1490],{"type":46,"tag":102,"props":1365,"children":1366},{"class":104,"line":105},[1367],{"type":46,"tag":102,"props":1368,"children":1369},{"style":109},[1370],{"type":51,"value":1371},"# 1. Validate — any issue with is_warning:false is a blocking error; fix before proceeding\n",{"type":46,"tag":102,"props":1373,"children":1374},{"class":104,"line":115},[1375,1379,1383,1388,1392,1396,1400,1404,1409,1413,1418,1422],{"type":46,"tag":102,"props":1376,"children":1377},{"style":198},[1378],{"type":51,"value":15},{"type":46,"tag":102,"props":1380,"children":1381},{"style":133},[1382],{"type":51,"value":538},{"type":46,"tag":102,"props":1384,"children":1385},{"style":133},[1386],{"type":51,"value":1387}," validate",{"type":46,"tag":102,"props":1389,"children":1390},{"style":144},[1391],{"type":51,"value":240},{"type":46,"tag":102,"props":1393,"children":1394},{"style":133},[1395],{"type":51,"value":836},{"type":46,"tag":102,"props":1397,"children":1398},{"style":248},[1399],{"type":51,"value":841},{"type":46,"tag":102,"props":1401,"children":1402},{"style":144},[1403],{"type":51,"value":846},{"type":46,"tag":102,"props":1405,"children":1406},{"style":133},[1407],{"type":51,"value":1408}," --branchid",{"type":46,"tag":102,"props":1410,"children":1411},{"style":144},[1412],{"type":51,"value":240},{"type":46,"tag":102,"props":1414,"children":1415},{"style":133},[1416],{"type":51,"value":1417},"branchI",{"type":46,"tag":102,"props":1419,"children":1420},{"style":248},[1421],{"type":51,"value":841},{"type":46,"tag":102,"props":1423,"children":1424},{"style":144},[1425],{"type":51,"value":256},{"type":46,"tag":102,"props":1427,"children":1428},{"class":104,"line":33},[1429],{"type":46,"tag":102,"props":1430,"children":1431},{"emptyLinePlaceholder":263},[1432],{"type":51,"value":266},{"type":46,"tag":102,"props":1434,"children":1435},{"class":104,"line":221},[1436],{"type":46,"tag":102,"props":1437,"children":1438},{"style":109},[1439],{"type":51,"value":1440},"# 2. Query the fields you changed — confirm no error and summary.row_count > 0\n",{"type":46,"tag":102,"props":1442,"children":1443},{"class":104,"line":259},[1444,1448,1453,1458,1462,1466,1471],{"type":46,"tag":102,"props":1445,"children":1446},{"style":198},[1447],{"type":51,"value":15},{"type":46,"tag":102,"props":1449,"children":1450},{"style":133},[1451],{"type":51,"value":1452}," query",{"type":46,"tag":102,"props":1454,"children":1455},{"style":133},[1456],{"type":51,"value":1457}," run",{"type":46,"tag":102,"props":1459,"children":1460},{"style":133},[1461],{"type":51,"value":1084},{"type":46,"tag":102,"props":1463,"children":1464},{"style":144},[1465],{"type":51,"value":1089},{"type":46,"tag":102,"props":1467,"children":1468},{"style":133},[1469],{"type":51,"value":1470},"{\"query\":{\"modelId\":\"\u003CmodelId>\",\"table\":\"your_view\",\"fields\":[\"your_view.new_dimension\",\"your_view.new_measure\"],\"limit\":10,\"join_paths_from_topic_name\":\"your_topic\"},\"branchId\":\"\u003CbranchId>\"}",{"type":46,"tag":102,"props":1472,"children":1473},{"style":144},[1474],{"type":51,"value":1147},{"type":46,"tag":102,"props":1476,"children":1477},{"class":104,"line":269},[1478],{"type":46,"tag":102,"props":1479,"children":1480},{"emptyLinePlaceholder":263},[1481],{"type":51,"value":266},{"type":46,"tag":102,"props":1483,"children":1484},{"class":104,"line":278},[1485],{"type":46,"tag":102,"props":1486,"children":1487},{"style":109},[1488],{"type":51,"value":1489},"# 3. Read it back — confirm the field is present (and not duplicated at a second path)\n",{"type":46,"tag":102,"props":1491,"children":1493},{"class":104,"line":1492},8,[1494,1498,1502,1507,1511,1515,1519,1523,1528,1533,1537,1541,1545,1549],{"type":46,"tag":102,"props":1495,"children":1496},{"style":198},[1497],{"type":51,"value":15},{"type":46,"tag":102,"props":1499,"children":1500},{"style":133},[1501],{"type":51,"value":538},{"type":46,"tag":102,"props":1503,"children":1504},{"style":133},[1505],{"type":51,"value":1506}," yaml-get",{"type":46,"tag":102,"props":1508,"children":1509},{"style":144},[1510],{"type":51,"value":240},{"type":46,"tag":102,"props":1512,"children":1513},{"style":133},[1514],{"type":51,"value":836},{"type":46,"tag":102,"props":1516,"children":1517},{"style":248},[1518],{"type":51,"value":841},{"type":46,"tag":102,"props":1520,"children":1521},{"style":144},[1522],{"type":51,"value":846},{"type":46,"tag":102,"props":1524,"children":1525},{"style":133},[1526],{"type":51,"value":1527}," --filename",{"type":46,"tag":102,"props":1529,"children":1530},{"style":133},[1531],{"type":51,"value":1532}," your_view.view",{"type":46,"tag":102,"props":1534,"children":1535},{"style":133},[1536],{"type":51,"value":1408},{"type":46,"tag":102,"props":1538,"children":1539},{"style":144},[1540],{"type":51,"value":240},{"type":46,"tag":102,"props":1542,"children":1543},{"style":133},[1544],{"type":51,"value":1417},{"type":46,"tag":102,"props":1546,"children":1547},{"style":248},[1548],{"type":51,"value":841},{"type":46,"tag":102,"props":1550,"children":1551},{"style":144},[1552],{"type":51,"value":256},{"type":46,"tag":54,"props":1554,"children":1555},{},[1556,1558,1564,1566,1572,1574,1580,1582,1588],{"type":51,"value":1557},"Spot-check that values look right (a ",{"type":46,"tag":75,"props":1559,"children":1561},{"className":1560},[],[1562],{"type":51,"value":1563},"sum",{"type":51,"value":1565}," isn't returning a ",{"type":46,"tag":75,"props":1567,"children":1569},{"className":1568},[],[1570],{"type":51,"value":1571},"count",{"type":51,"value":1573},"; booleans read true\u002Ffalse), and if a field references another view include fields from both to confirm the join resolves. See ",{"type":46,"tag":75,"props":1575,"children":1577},{"className":1576},[],[1578],{"type":51,"value":1579},"references\u002Fvalidation-and-testing.md",{"type":51,"value":1581}," for join-path testing, natural-language validation via ",{"type":46,"tag":75,"props":1583,"children":1585},{"className":1584},[],[1586],{"type":51,"value":1587},"omni ai job-submit",{"type":51,"value":1589},", the full results checklist, and duplicate-file recovery.",{"type":46,"tag":799,"props":1591,"children":1593},{"id":1592},"step-3-ship-the-branch",[1594],{"type":51,"value":1595},"Step 3: Ship the Branch",{"type":46,"tag":60,"props":1597,"children":1598},{},[1599],{"type":46,"tag":54,"props":1600,"children":1601},{},[1602,1607],{"type":46,"tag":67,"props":1603,"children":1604},{},[1605],{"type":51,"value":1606},"Important",{"type":51,"value":1608},": Always ask the user for confirmation before shipping. Changes applied to the production model cannot be easily undone. Only ship after validation and testing pass (Step 2).",{"type":46,"tag":54,"props":1610,"children":1611},{},[1612,1614,1620,1622,1628,1630,1636,1638,1643,1645,1651,1653,1658],{"type":51,"value":1613},"Check whether the model is git-connected — ",{"type":46,"tag":75,"props":1615,"children":1617},{"className":1616},[],[1618],{"type":51,"value":1619},"omni models git-get \u003CmodelId>",{"type":51,"value":1621},". A config with ",{"type":46,"tag":75,"props":1623,"children":1625},{"className":1624},[],[1626],{"type":51,"value":1627},"sshUrl",{"type":51,"value":1629}," \u002F ",{"type":46,"tag":75,"props":1631,"children":1633},{"className":1632},[],[1634],{"type":51,"value":1635},"baseBranch",{"type":51,"value":1637}," → git-connected → ",{"type":46,"tag":67,"props":1639,"children":1640},{},[1641],{"type":51,"value":1642},"Path A",{"type":51,"value":1644}," (open\u002Fupdate a PR); a ",{"type":46,"tag":75,"props":1646,"children":1648},{"className":1647},[],[1649],{"type":51,"value":1650},"404",{"type":51,"value":1652},"\u002Fno config → not git-connected → ",{"type":46,"tag":67,"props":1654,"children":1655},{},[1656],{"type":51,"value":1657},"Path B",{"type":51,"value":1659}," (merge directly in Omni).",{"type":46,"tag":1661,"props":1662,"children":1664},"h4",{"id":1663},"path-a-git-connected-open-or-update-a-pr",[1665],{"type":51,"value":1666},"Path A — Git-connected: open or update a PR",{"type":46,"tag":54,"props":1668,"children":1669},{},[1670],{"type":51,"value":1671},"Push the branch contents to git. Creates a new git branch + PR if one doesn't exist; otherwise updates the existing PR:",{"type":46,"tag":91,"props":1673,"children":1675},{"className":93,"code":1674,"language":95,"meta":96,"style":96},"omni models commit \u003CmodelId> --body '{\n  \"branch_id\": \"\u003CbranchId>\",\n  \"commit_message\": \"Add my_new_view with status dimension and count measure\"\n}'\n",[1676],{"type":46,"tag":75,"props":1677,"children":1678},{"__ignoreMap":96},[1679,1723,1731,1739],{"type":46,"tag":102,"props":1680,"children":1681},{"class":104,"line":105},[1682,1686,1690,1695,1699,1703,1707,1711,1715,1719],{"type":46,"tag":102,"props":1683,"children":1684},{"style":198},[1685],{"type":51,"value":15},{"type":46,"tag":102,"props":1687,"children":1688},{"style":133},[1689],{"type":51,"value":538},{"type":46,"tag":102,"props":1691,"children":1692},{"style":133},[1693],{"type":51,"value":1694}," commit",{"type":46,"tag":102,"props":1696,"children":1697},{"style":144},[1698],{"type":51,"value":240},{"type":46,"tag":102,"props":1700,"children":1701},{"style":133},[1702],{"type":51,"value":836},{"type":46,"tag":102,"props":1704,"children":1705},{"style":248},[1706],{"type":51,"value":841},{"type":46,"tag":102,"props":1708,"children":1709},{"style":144},[1710],{"type":51,"value":846},{"type":46,"tag":102,"props":1712,"children":1713},{"style":133},[1714],{"type":51,"value":1084},{"type":46,"tag":102,"props":1716,"children":1717},{"style":144},[1718],{"type":51,"value":1089},{"type":46,"tag":102,"props":1720,"children":1721},{"style":133},[1722],{"type":51,"value":1094},{"type":46,"tag":102,"props":1724,"children":1725},{"class":104,"line":115},[1726],{"type":46,"tag":102,"props":1727,"children":1728},{"style":133},[1729],{"type":51,"value":1730},"  \"branch_id\": \"\u003CbranchId>\",\n",{"type":46,"tag":102,"props":1732,"children":1733},{"class":104,"line":33},[1734],{"type":46,"tag":102,"props":1735,"children":1736},{"style":133},[1737],{"type":51,"value":1738},"  \"commit_message\": \"Add my_new_view with status dimension and count measure\"\n",{"type":46,"tag":102,"props":1740,"children":1741},{"class":104,"line":221},[1742,1746],{"type":46,"tag":102,"props":1743,"children":1744},{"style":133},[1745],{"type":51,"value":1142},{"type":46,"tag":102,"props":1747,"children":1748},{"style":144},[1749],{"type":51,"value":1147},{"type":46,"tag":54,"props":1751,"children":1752},{},[1753,1755,1761,1763,1768,1770,1776,1778,1784,1786,1792],{"type":51,"value":1754},"Surface the returned ",{"type":46,"tag":75,"props":1756,"children":1758},{"className":1757},[],[1759],{"type":51,"value":1760},"pr_url",{"type":51,"value":1762}," to the user. The reviewer merges the PR in your git host; changes flow back to ",{"type":46,"tag":75,"props":1764,"children":1766},{"className":1765},[],[1767],{"type":51,"value":1635},{"type":51,"value":1769}," on the next sync. Run ",{"type":46,"tag":75,"props":1771,"children":1773},{"className":1772},[],[1774],{"type":51,"value":1775},"omni models commit --help",{"type":51,"value":1777}," for optional body flags (",{"type":46,"tag":75,"props":1779,"children":1781},{"className":1780},[],[1782],{"type":51,"value":1783},"allow_branch_exists",{"type":51,"value":1785},", ",{"type":46,"tag":75,"props":1787,"children":1789},{"className":1788},[],[1790],{"type":51,"value":1791},"require_branch_exists",{"type":51,"value":1793},") when you need to enforce open-only or update-only behavior.",{"type":46,"tag":1661,"props":1795,"children":1797},{"id":1796},"path-b-not-git-connected-merge-in-omni",[1798],{"type":51,"value":1799},"Path B — Not git-connected: merge in Omni",{"type":46,"tag":91,"props":1801,"children":1803},{"className":93,"code":1802,"language":95,"meta":96,"style":96},"omni models merge-branch \u003CmodelId> \u003CbranchName>\n",[1804],{"type":46,"tag":75,"props":1805,"children":1806},{"__ignoreMap":96},[1807],{"type":46,"tag":102,"props":1808,"children":1809},{"class":104,"line":105},[1810,1814,1818,1823,1827,1831,1835,1839,1843,1848,1852],{"type":46,"tag":102,"props":1811,"children":1812},{"style":198},[1813],{"type":51,"value":15},{"type":46,"tag":102,"props":1815,"children":1816},{"style":133},[1817],{"type":51,"value":538},{"type":46,"tag":102,"props":1819,"children":1820},{"style":133},[1821],{"type":51,"value":1822}," merge-branch",{"type":46,"tag":102,"props":1824,"children":1825},{"style":144},[1826],{"type":51,"value":240},{"type":46,"tag":102,"props":1828,"children":1829},{"style":133},[1830],{"type":51,"value":836},{"type":46,"tag":102,"props":1832,"children":1833},{"style":248},[1834],{"type":51,"value":841},{"type":46,"tag":102,"props":1836,"children":1837},{"style":144},[1838],{"type":51,"value":846},{"type":46,"tag":102,"props":1840,"children":1841},{"style":144},[1842],{"type":51,"value":240},{"type":46,"tag":102,"props":1844,"children":1845},{"style":133},[1846],{"type":51,"value":1847},"branchNam",{"type":46,"tag":102,"props":1849,"children":1850},{"style":248},[1851],{"type":51,"value":251},{"type":46,"tag":102,"props":1853,"children":1854},{"style":144},[1855],{"type":51,"value":256},{"type":46,"tag":1661,"props":1857,"children":1859},{"id":1858},"after-the-merge-verify-net-new-topics-and-views-both-paths",[1860],{"type":51,"value":1861},"After the merge — verify net-new topics and views (both paths)",{"type":46,"tag":54,"props":1863,"children":1864},{},[1865,1867,1872,1874,1879,1881,1886,1888,1893,1895,1901],{"type":51,"value":1866},"After the merge, Omni regenerates the default branch from its own (authoritative) model state — re-serializing to canonical form, and adding a normalization commit only when the merged git content ",{"type":46,"tag":353,"props":1868,"children":1869},{},[1870],{"type":51,"value":1871},"differs",{"type":51,"value":1873}," from that state. Content authored through the Omni APIs is already canonical, so a clean merge often adds ",{"type":46,"tag":67,"props":1875,"children":1876},{},[1877],{"type":51,"value":1878},"no extra commit",{"type":51,"value":1880}," — don't go hunting for one; verify by resolution instead. (A non-git merge promotes the branch into the shared model.) Either way — and ",{"type":46,"tag":67,"props":1882,"children":1883},{},[1884],{"type":51,"value":1885},"especially for net-new topics or views",{"type":51,"value":1887}," — confirm the files resolve against the ",{"type":46,"tag":67,"props":1889,"children":1890},{},[1891],{"type":51,"value":1892},"production",{"type":51,"value":1894}," model (no ",{"type":46,"tag":75,"props":1896,"children":1898},{"className":1897},[],[1899],{"type":51,"value":1900},"--branchid",{"type":51,"value":1902},"):",{"type":46,"tag":91,"props":1904,"children":1906},{"className":93,"code":1905,"language":95,"meta":96,"style":96},"# 1. Files resolve in production — the new view appears \u002F the new topic resolves\nomni models get-views \u003CmodelId>                 # new view is listed\nomni models get-topic \u003CmodelId> \u003CtopicName>     # new topic resolves (base_view_name + join_via_map present)\n\n# 2. Production model validates — no blocking errors (any is_warning:false is blocking)\nomni models validate \u003CmodelId>\n\n# 3. Run at least one semantic query against the new topic\u002Fview\nomni query run --body '{\"query\":{\"modelId\":\"\u003CmodelId>\",\"table\":\"\u003Cbase_view>\",\"fields\":[\"\u003Cbase_view>.\u003Cfield>\"],\"limit\":10,\"join_paths_from_topic_name\":\"\u003CtopicName>\"}}'\n",[1907],{"type":46,"tag":75,"props":1908,"children":1909},{"__ignoreMap":96},[1910,1918,1955,2009,2016,2024,2055,2062,2070],{"type":46,"tag":102,"props":1911,"children":1912},{"class":104,"line":105},[1913],{"type":46,"tag":102,"props":1914,"children":1915},{"style":109},[1916],{"type":51,"value":1917},"# 1. Files resolve in production — the new view appears \u002F the new topic resolves\n",{"type":46,"tag":102,"props":1919,"children":1920},{"class":104,"line":115},[1921,1925,1929,1934,1938,1942,1946,1950],{"type":46,"tag":102,"props":1922,"children":1923},{"style":198},[1924],{"type":51,"value":15},{"type":46,"tag":102,"props":1926,"children":1927},{"style":133},[1928],{"type":51,"value":538},{"type":46,"tag":102,"props":1930,"children":1931},{"style":133},[1932],{"type":51,"value":1933}," get-views",{"type":46,"tag":102,"props":1935,"children":1936},{"style":144},[1937],{"type":51,"value":240},{"type":46,"tag":102,"props":1939,"children":1940},{"style":133},[1941],{"type":51,"value":836},{"type":46,"tag":102,"props":1943,"children":1944},{"style":248},[1945],{"type":51,"value":841},{"type":46,"tag":102,"props":1947,"children":1948},{"style":144},[1949],{"type":51,"value":846},{"type":46,"tag":102,"props":1951,"children":1952},{"style":109},[1953],{"type":51,"value":1954},"                 # new view is listed\n",{"type":46,"tag":102,"props":1956,"children":1957},{"class":104,"line":33},[1958,1962,1966,1971,1975,1979,1983,1987,1991,1996,2000,2004],{"type":46,"tag":102,"props":1959,"children":1960},{"style":198},[1961],{"type":51,"value":15},{"type":46,"tag":102,"props":1963,"children":1964},{"style":133},[1965],{"type":51,"value":538},{"type":46,"tag":102,"props":1967,"children":1968},{"style":133},[1969],{"type":51,"value":1970}," get-topic",{"type":46,"tag":102,"props":1972,"children":1973},{"style":144},[1974],{"type":51,"value":240},{"type":46,"tag":102,"props":1976,"children":1977},{"style":133},[1978],{"type":51,"value":836},{"type":46,"tag":102,"props":1980,"children":1981},{"style":248},[1982],{"type":51,"value":841},{"type":46,"tag":102,"props":1984,"children":1985},{"style":144},[1986],{"type":51,"value":846},{"type":46,"tag":102,"props":1988,"children":1989},{"style":144},[1990],{"type":51,"value":240},{"type":46,"tag":102,"props":1992,"children":1993},{"style":133},[1994],{"type":51,"value":1995},"topicNam",{"type":46,"tag":102,"props":1997,"children":1998},{"style":248},[1999],{"type":51,"value":251},{"type":46,"tag":102,"props":2001,"children":2002},{"style":144},[2003],{"type":51,"value":846},{"type":46,"tag":102,"props":2005,"children":2006},{"style":109},[2007],{"type":51,"value":2008},"     # new topic resolves (base_view_name + join_via_map present)\n",{"type":46,"tag":102,"props":2010,"children":2011},{"class":104,"line":221},[2012],{"type":46,"tag":102,"props":2013,"children":2014},{"emptyLinePlaceholder":263},[2015],{"type":51,"value":266},{"type":46,"tag":102,"props":2017,"children":2018},{"class":104,"line":259},[2019],{"type":46,"tag":102,"props":2020,"children":2021},{"style":109},[2022],{"type":51,"value":2023},"# 2. Production model validates — no blocking errors (any is_warning:false is blocking)\n",{"type":46,"tag":102,"props":2025,"children":2026},{"class":104,"line":269},[2027,2031,2035,2039,2043,2047,2051],{"type":46,"tag":102,"props":2028,"children":2029},{"style":198},[2030],{"type":51,"value":15},{"type":46,"tag":102,"props":2032,"children":2033},{"style":133},[2034],{"type":51,"value":538},{"type":46,"tag":102,"props":2036,"children":2037},{"style":133},[2038],{"type":51,"value":1387},{"type":46,"tag":102,"props":2040,"children":2041},{"style":144},[2042],{"type":51,"value":240},{"type":46,"tag":102,"props":2044,"children":2045},{"style":133},[2046],{"type":51,"value":836},{"type":46,"tag":102,"props":2048,"children":2049},{"style":248},[2050],{"type":51,"value":841},{"type":46,"tag":102,"props":2052,"children":2053},{"style":144},[2054],{"type":51,"value":256},{"type":46,"tag":102,"props":2056,"children":2057},{"class":104,"line":278},[2058],{"type":46,"tag":102,"props":2059,"children":2060},{"emptyLinePlaceholder":263},[2061],{"type":51,"value":266},{"type":46,"tag":102,"props":2063,"children":2064},{"class":104,"line":1492},[2065],{"type":46,"tag":102,"props":2066,"children":2067},{"style":109},[2068],{"type":51,"value":2069},"# 3. Run at least one semantic query against the new topic\u002Fview\n",{"type":46,"tag":102,"props":2071,"children":2073},{"class":104,"line":2072},9,[2074,2078,2082,2086,2090,2094,2099],{"type":46,"tag":102,"props":2075,"children":2076},{"style":198},[2077],{"type":51,"value":15},{"type":46,"tag":102,"props":2079,"children":2080},{"style":133},[2081],{"type":51,"value":1452},{"type":46,"tag":102,"props":2083,"children":2084},{"style":133},[2085],{"type":51,"value":1457},{"type":46,"tag":102,"props":2087,"children":2088},{"style":133},[2089],{"type":51,"value":1084},{"type":46,"tag":102,"props":2091,"children":2092},{"style":144},[2093],{"type":51,"value":1089},{"type":46,"tag":102,"props":2095,"children":2096},{"style":133},[2097],{"type":51,"value":2098},"{\"query\":{\"modelId\":\"\u003CmodelId>\",\"table\":\"\u003Cbase_view>\",\"fields\":[\"\u003Cbase_view>.\u003Cfield>\"],\"limit\":10,\"join_paths_from_topic_name\":\"\u003CtopicName>\"}}",{"type":46,"tag":102,"props":2100,"children":2101},{"style":144},[2102],{"type":51,"value":1147},{"type":46,"tag":54,"props":2104,"children":2105},{},[2106,2108,2114,2116,2122,2124,2130,2132,2138,2139,2144,2146,2152],{"type":51,"value":2107},"In the query response, confirm ",{"type":46,"tag":75,"props":2109,"children":2111},{"className":2110},[],[2112],{"type":51,"value":2113},"summary.missing_fields",{"type":51,"value":2115}," is ",{"type":46,"tag":75,"props":2117,"children":2119},{"className":2118},[],[2120],{"type":51,"value":2121},"[]",{"type":51,"value":2123}," (and ",{"type":46,"tag":75,"props":2125,"children":2127},{"className":2126},[],[2128],{"type":51,"value":2129},"summary.invalid_calculations",{"type":51,"value":2131}," is empty — note it comes back as ",{"type":46,"tag":75,"props":2133,"children":2135},{"className":2134},[],[2136],{"type":51,"value":2137},"{}",{"type":51,"value":1211},{"type":46,"tag":75,"props":2140,"children":2142},{"className":2141},[],[2143],{"type":51,"value":2121},{"type":51,"value":2145},"). A non-empty ",{"type":46,"tag":75,"props":2147,"children":2149},{"className":2148},[],[2150],{"type":51,"value":2151},"missing_fields",{"type":51,"value":2153}," means a field didn't resolve — the signature of a model file dropped or renamed during the merge. If anything is missing, re-author it through Omni; never patch it back by hand in git.",{"type":46,"tag":84,"props":2155,"children":2157},{"id":2156},"yaml-file-types",[2158],{"type":51,"value":2159},"YAML File Types",{"type":46,"tag":2161,"props":2162,"children":2163},"table",{},[2164,2188],{"type":46,"tag":2165,"props":2166,"children":2167},"thead",{},[2168],{"type":46,"tag":2169,"props":2170,"children":2171},"tr",{},[2172,2178,2183],{"type":46,"tag":2173,"props":2174,"children":2175},"th",{},[2176],{"type":51,"value":2177},"Type",{"type":46,"tag":2173,"props":2179,"children":2180},{},[2181],{"type":51,"value":2182},"Extension",{"type":46,"tag":2173,"props":2184,"children":2185},{},[2186],{"type":51,"value":2187},"Purpose",{"type":46,"tag":2189,"props":2190,"children":2191},"tbody",{},[2192,2215,2237],{"type":46,"tag":2169,"props":2193,"children":2194},{},[2195,2201,2210],{"type":46,"tag":2196,"props":2197,"children":2198},"td",{},[2199],{"type":51,"value":2200},"View",{"type":46,"tag":2196,"props":2202,"children":2203},{},[2204],{"type":46,"tag":75,"props":2205,"children":2207},{"className":2206},[],[2208],{"type":51,"value":2209},".view",{"type":46,"tag":2196,"props":2211,"children":2212},{},[2213],{"type":51,"value":2214},"Dimensions, measures, filters for a table",{"type":46,"tag":2169,"props":2216,"children":2217},{},[2218,2223,2232],{"type":46,"tag":2196,"props":2219,"children":2220},{},[2221],{"type":51,"value":2222},"Topic",{"type":46,"tag":2196,"props":2224,"children":2225},{},[2226],{"type":46,"tag":75,"props":2227,"children":2229},{"className":2228},[],[2230],{"type":51,"value":2231},".topic",{"type":46,"tag":2196,"props":2233,"children":2234},{},[2235],{"type":51,"value":2236},"Joins views into a queryable unit",{"type":46,"tag":2169,"props":2238,"children":2239},{},[2240,2245,2250],{"type":46,"tag":2196,"props":2241,"children":2242},{},[2243],{"type":51,"value":2244},"Relationships",{"type":46,"tag":2196,"props":2246,"children":2247},{},[2248],{"type":51,"value":2249},"(special)",{"type":46,"tag":2196,"props":2251,"children":2252},{},[2253],{"type":51,"value":2254},"Global join definitions",{"type":46,"tag":54,"props":2256,"children":2257},{},[2258,2260,2266,2268,2273],{"type":51,"value":2259},"Write with ",{"type":46,"tag":75,"props":2261,"children":2263},{"className":2262},[],[2264],{"type":51,"value":2265},"mode: \"extension\"",{"type":51,"value":2267}," (shared model layer). To delete a file, send empty ",{"type":46,"tag":75,"props":2269,"children":2271},{"className":2270},[],[2272],{"type":51,"value":19},{"type":51,"value":386},{"type":46,"tag":84,"props":2275,"children":2277},{"id":2276},"writing-views",[2278],{"type":51,"value":2279},"Writing Views",{"type":46,"tag":60,"props":2281,"children":2282},{},[2283],{"type":46,"tag":54,"props":2284,"children":2285},{},[2286,2299,2301,2307,2308,2314,2315,2321,2323,2329,2331,2336],{"type":46,"tag":67,"props":2287,"children":2288},{},[2289,2291,2297],{"type":51,"value":2290},"Every view that participates in joins MUST have a real ",{"type":46,"tag":75,"props":2292,"children":2294},{"className":2293},[],[2295],{"type":51,"value":2296},"primary_key: true",{"type":51,"value":2298}," dimension.",{"type":51,"value":2300}," Without a genuine row-unique primary key, queries that join to this view can produce fanout errors or incorrect aggregations. Use the table's natural unique identifier (e.g., ",{"type":46,"tag":75,"props":2302,"children":2304},{"className":2303},[],[2305],{"type":51,"value":2306},"id",{"type":51,"value":1785},{"type":46,"tag":75,"props":2309,"children":2311},{"className":2310},[],[2312],{"type":51,"value":2313},"order_id",{"type":51,"value":1785},{"type":46,"tag":75,"props":2316,"children":2318},{"className":2317},[],[2319],{"type":51,"value":2320},"user_id",{"type":51,"value":2322},"). If no single column is unique, build a composite key from row-level columns that are jointly unique, for example ",{"type":46,"tag":75,"props":2324,"children":2326},{"className":2325},[],[2327],{"type":51,"value":2328},"sql: ${order_id} || '-' || ${line_number}",{"type":51,"value":2330},". If you cannot define a row-unique expression, do not mark a dimension as ",{"type":46,"tag":75,"props":2332,"children":2334},{"className":2333},[],[2335],{"type":51,"value":2296},{"type":51,"value":2337}," yet; fix the grain first or avoid joining the view until a real key exists.",{"type":46,"tag":60,"props":2339,"children":2340},{},[2341],{"type":46,"tag":54,"props":2342,"children":2343},{},[2344,2349,2351,2356,2358,2363,2364,2370,2372,2377,2379,2388,2390,2395,2397,2402,2404,2409,2411,2417,2419,2424,2426,2431,2433,2438,2440,2445,2447,2452,2454,2460],{"type":46,"tag":67,"props":2345,"children":2346},{},[2347],{"type":51,"value":2348},"Why the PK matters mechanically — symmetric aggregates.",{"type":51,"value":2350}," Omni keeps ",{"type":46,"tag":75,"props":2352,"children":2354},{"className":2353},[],[2355],{"type":51,"value":1563},{"type":51,"value":2357},"\u002F",{"type":46,"tag":75,"props":2359,"children":2361},{"className":2360},[],[2362],{"type":51,"value":1571},{"type":51,"value":2357},{"type":46,"tag":75,"props":2365,"children":2367},{"className":2366},[],[2368],{"type":51,"value":2369},"avg",{"type":51,"value":2371}," correct under a one-to-many join by ",{"type":46,"tag":67,"props":2373,"children":2374},{},[2375],{"type":51,"value":2376},"deduplicating on the primary key",{"type":51,"value":2378}," (",{"type":46,"tag":2380,"props":2381,"children":2385},"a",{"href":2382,"rel":2383},"https:\u002F\u002Fdocs.omni.co\u002Fanalyze-explore\u002Fsql\u002Fsymmetric-aggregates",[2384],"nofollow",[2386],{"type":51,"value":2387},"symmetric aggregates",{"type":51,"value":2389},"). A view ",{"type":46,"tag":67,"props":2391,"children":2392},{},[2393],{"type":51,"value":2394},"missing",{"type":51,"value":2396}," a PK can't be made symmetric, so a ",{"type":46,"tag":75,"props":2398,"children":2400},{"className":2399},[],[2401],{"type":51,"value":1571},{"type":51,"value":2403},"-of-rows measure on it ",{"type":46,"tag":67,"props":2405,"children":2406},{},[2407],{"type":51,"value":2408},"inflates",{"type":51,"value":2410}," whenever a join duplicates its rows; a ",{"type":46,"tag":75,"props":2412,"children":2414},{"className":2413},[],[2415],{"type":51,"value":2416},"count_distinct",{"type":51,"value":2418}," measure survives only because it dedupes by construction. ",{"type":46,"tag":67,"props":2420,"children":2421},{},[2422],{"type":51,"value":2423},"Diagnostic heuristic:",{"type":51,"value":2425}," when a measure exceeds a measure it should be a subset of (e.g. ",{"type":46,"tag":353,"props":2427,"children":2428},{},[2429],{"type":51,"value":2430},"\"sessions that viewed a product\" > \"total sessions\"",{"type":51,"value":2432},"), suspect fanout from a non-distinct count on a view with ",{"type":46,"tag":67,"props":2434,"children":2435},{},[2436],{"type":51,"value":2437},"no\u002Fweak PK",{"type":51,"value":2439}," — fix the PK, don't patch the number. Audit ",{"type":46,"tag":353,"props":2441,"children":2442},{},[2443],{"type":51,"value":2444},"every",{"type":51,"value":2446}," base view for a PK (one missing PK is enough to break one viz). Note that a dashboard ",{"type":46,"tag":67,"props":2448,"children":2449},{},[2450],{"type":51,"value":2451},"filter\u002Fcontrol can pull in a join the topic never declared",{"type":51,"value":2453},", via the global ",{"type":46,"tag":75,"props":2455,"children":2457},{"className":2456},[],[2458],{"type":51,"value":2459},"relationships",{"type":51,"value":2461}," graph, to satisfy its field — so even a \"single-topic\" tile can fan out; the PK is what keeps the aggregates safe regardless.",{"type":46,"tag":799,"props":2463,"children":2465},{"id":2464},"basic-view",[2466],{"type":51,"value":2467},"Basic View",{"type":46,"tag":91,"props":2469,"children":2472},{"className":2470,"code":2471,"language":19,"meta":96,"style":96},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","dimensions:\n  order_id:\n    primary_key: true\n  status:\n    label: Order Status\n  created_at:\n    label: Created Date\nmeasures:\n  count:\n    aggregate_type: count\n  total_revenue:\n    sql: ${sale_price}\n    aggregate_type: sum\n    format: currency_2\n",[2473],{"type":46,"tag":75,"props":2474,"children":2475},{"__ignoreMap":96},[2476,2490,2502,2521,2533,2550,2562,2578,2590,2602,2620,2633,2651,2668],{"type":46,"tag":102,"props":2477,"children":2478},{"class":104,"line":105},[2479,2485],{"type":46,"tag":102,"props":2480,"children":2482},{"style":2481},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[2483],{"type":51,"value":2484},"dimensions",{"type":46,"tag":102,"props":2486,"children":2487},{"style":144},[2488],{"type":51,"value":2489},":\n",{"type":46,"tag":102,"props":2491,"children":2492},{"class":104,"line":115},[2493,2498],{"type":46,"tag":102,"props":2494,"children":2495},{"style":2481},[2496],{"type":51,"value":2497},"  order_id",{"type":46,"tag":102,"props":2499,"children":2500},{"style":144},[2501],{"type":51,"value":2489},{"type":46,"tag":102,"props":2503,"children":2504},{"class":104,"line":33},[2505,2510,2515],{"type":46,"tag":102,"props":2506,"children":2507},{"style":2481},[2508],{"type":51,"value":2509},"    primary_key",{"type":46,"tag":102,"props":2511,"children":2512},{"style":144},[2513],{"type":51,"value":2514},":",{"type":46,"tag":102,"props":2516,"children":2518},{"style":2517},"--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC",[2519],{"type":51,"value":2520}," true\n",{"type":46,"tag":102,"props":2522,"children":2523},{"class":104,"line":221},[2524,2529],{"type":46,"tag":102,"props":2525,"children":2526},{"style":2481},[2527],{"type":51,"value":2528},"  status",{"type":46,"tag":102,"props":2530,"children":2531},{"style":144},[2532],{"type":51,"value":2489},{"type":46,"tag":102,"props":2534,"children":2535},{"class":104,"line":259},[2536,2541,2545],{"type":46,"tag":102,"props":2537,"children":2538},{"style":2481},[2539],{"type":51,"value":2540},"    label",{"type":46,"tag":102,"props":2542,"children":2543},{"style":144},[2544],{"type":51,"value":2514},{"type":46,"tag":102,"props":2546,"children":2547},{"style":133},[2548],{"type":51,"value":2549}," Order Status\n",{"type":46,"tag":102,"props":2551,"children":2552},{"class":104,"line":269},[2553,2558],{"type":46,"tag":102,"props":2554,"children":2555},{"style":2481},[2556],{"type":51,"value":2557},"  created_at",{"type":46,"tag":102,"props":2559,"children":2560},{"style":144},[2561],{"type":51,"value":2489},{"type":46,"tag":102,"props":2563,"children":2564},{"class":104,"line":278},[2565,2569,2573],{"type":46,"tag":102,"props":2566,"children":2567},{"style":2481},[2568],{"type":51,"value":2540},{"type":46,"tag":102,"props":2570,"children":2571},{"style":144},[2572],{"type":51,"value":2514},{"type":46,"tag":102,"props":2574,"children":2575},{"style":133},[2576],{"type":51,"value":2577}," Created Date\n",{"type":46,"tag":102,"props":2579,"children":2580},{"class":104,"line":1492},[2581,2586],{"type":46,"tag":102,"props":2582,"children":2583},{"style":2481},[2584],{"type":51,"value":2585},"measures",{"type":46,"tag":102,"props":2587,"children":2588},{"style":144},[2589],{"type":51,"value":2489},{"type":46,"tag":102,"props":2591,"children":2592},{"class":104,"line":2072},[2593,2598],{"type":46,"tag":102,"props":2594,"children":2595},{"style":2481},[2596],{"type":51,"value":2597},"  count",{"type":46,"tag":102,"props":2599,"children":2600},{"style":144},[2601],{"type":51,"value":2489},{"type":46,"tag":102,"props":2603,"children":2605},{"class":104,"line":2604},10,[2606,2611,2615],{"type":46,"tag":102,"props":2607,"children":2608},{"style":2481},[2609],{"type":51,"value":2610},"    aggregate_type",{"type":46,"tag":102,"props":2612,"children":2613},{"style":144},[2614],{"type":51,"value":2514},{"type":46,"tag":102,"props":2616,"children":2617},{"style":133},[2618],{"type":51,"value":2619}," count\n",{"type":46,"tag":102,"props":2621,"children":2623},{"class":104,"line":2622},11,[2624,2629],{"type":46,"tag":102,"props":2625,"children":2626},{"style":2481},[2627],{"type":51,"value":2628},"  total_revenue",{"type":46,"tag":102,"props":2630,"children":2631},{"style":144},[2632],{"type":51,"value":2489},{"type":46,"tag":102,"props":2634,"children":2636},{"class":104,"line":2635},12,[2637,2642,2646],{"type":46,"tag":102,"props":2638,"children":2639},{"style":2481},[2640],{"type":51,"value":2641},"    sql",{"type":46,"tag":102,"props":2643,"children":2644},{"style":144},[2645],{"type":51,"value":2514},{"type":46,"tag":102,"props":2647,"children":2648},{"style":133},[2649],{"type":51,"value":2650}," ${sale_price}\n",{"type":46,"tag":102,"props":2652,"children":2654},{"class":104,"line":2653},13,[2655,2659,2663],{"type":46,"tag":102,"props":2656,"children":2657},{"style":2481},[2658],{"type":51,"value":2610},{"type":46,"tag":102,"props":2660,"children":2661},{"style":144},[2662],{"type":51,"value":2514},{"type":46,"tag":102,"props":2664,"children":2665},{"style":133},[2666],{"type":51,"value":2667}," sum\n",{"type":46,"tag":102,"props":2669,"children":2671},{"class":104,"line":2670},14,[2672,2677,2681],{"type":46,"tag":102,"props":2673,"children":2674},{"style":2481},[2675],{"type":51,"value":2676},"    format",{"type":46,"tag":102,"props":2678,"children":2679},{"style":144},[2680],{"type":51,"value":2514},{"type":46,"tag":102,"props":2682,"children":2683},{"style":133},[2684],{"type":51,"value":2685}," currency_2\n",{"type":46,"tag":799,"props":2687,"children":2689},{"id":2688},"understanding-schema-layer-vs-extension-layer",[2690],{"type":51,"value":2691},"Understanding Schema Layer vs Extension Layer",{"type":46,"tag":54,"props":2693,"children":2694},{},[2695,2697,2702,2704,2709],{"type":51,"value":2696},"When you create a view, Omni separates ",{"type":46,"tag":67,"props":2698,"children":2699},{},[2700],{"type":51,"value":2701},"schema",{"type":51,"value":2703}," (database structure) from ",{"type":46,"tag":67,"props":2705,"children":2706},{},[2707],{"type":51,"value":2708},"model",{"type":51,"value":2710}," (your business logic):",{"type":46,"tag":672,"props":2712,"children":2713},{},[2714,2724],{"type":46,"tag":444,"props":2715,"children":2716},{},[2717,2722],{"type":46,"tag":67,"props":2718,"children":2719},{},[2720],{"type":51,"value":2721},"Schema layer",{"type":51,"value":2723},": Auto-generated base dimensions, one per column. Types come from the database. Read-only, synced via schema refresh.",{"type":46,"tag":444,"props":2725,"children":2726},{},[2727,2732],{"type":46,"tag":67,"props":2728,"children":2729},{},[2730],{"type":51,"value":2731},"Extension layer",{"type":51,"value":2733},": Your custom YAML. Can override base dimensions, add new dimensions\u002Fmeasures, hide columns, add business logic.",{"type":46,"tag":54,"props":2735,"children":2736},{},[2737,2739,2744,2746,2751],{"type":51,"value":2738},"When both layers exist for a field with the same name, ",{"type":46,"tag":67,"props":2740,"children":2741},{},[2742],{"type":51,"value":2743},"your extension definition wins",{"type":51,"value":2745}," but ",{"type":46,"tag":67,"props":2747,"children":2748},{},[2749],{"type":51,"value":2750},"type information comes from the schema layer",{"type":51,"value":386},{"type":46,"tag":54,"props":2753,"children":2754},{},[2755,2760,2762,2768,2770,2776],{"type":46,"tag":67,"props":2756,"children":2757},{},[2758],{"type":51,"value":2759},"Example",{"type":51,"value":2761},": Table has columns ",{"type":46,"tag":75,"props":2763,"children":2765},{"className":2764},[],[2766],{"type":51,"value":2767},"created_at",{"type":51,"value":2769}," (DATE) and ",{"type":46,"tag":75,"props":2771,"children":2773},{"className":2772},[],[2774],{"type":51,"value":2775},"revenue",{"type":51,"value":2777}," (NUMERIC).",{"type":46,"tag":91,"props":2779,"children":2781},{"className":2470,"code":2780,"language":19,"meta":96,"style":96},"# Schema layer (auto-generated)\ndimensions:\n  created_at: {}  # type: DATE, auto-generates timeframes\n  revenue: {}     # type: NUMERIC\n\n# Extension layer (your YAML)\ndimensions:\n  created_at:\n    label: \"Order Created\"\n    description: \"When the order was placed\"\n\n  revenue:\n    hidden: true  # Hide the raw column\n\nmeasures:\n  total_revenue:\n    sql: ${revenue}\n    aggregate_type: sum\n    format: currency_2\n",[2782],{"type":46,"tag":75,"props":2783,"children":2784},{"__ignoreMap":96},[2785,2793,2804,2825,2846,2853,2861,2872,2883,2907,2932,2939,2950,2972,2979,2991,3003,3020,3036],{"type":46,"tag":102,"props":2786,"children":2787},{"class":104,"line":105},[2788],{"type":46,"tag":102,"props":2789,"children":2790},{"style":109},[2791],{"type":51,"value":2792},"# Schema layer (auto-generated)\n",{"type":46,"tag":102,"props":2794,"children":2795},{"class":104,"line":115},[2796,2800],{"type":46,"tag":102,"props":2797,"children":2798},{"style":2481},[2799],{"type":51,"value":2484},{"type":46,"tag":102,"props":2801,"children":2802},{"style":144},[2803],{"type":51,"value":2489},{"type":46,"tag":102,"props":2805,"children":2806},{"class":104,"line":33},[2807,2811,2815,2820],{"type":46,"tag":102,"props":2808,"children":2809},{"style":2481},[2810],{"type":51,"value":2557},{"type":46,"tag":102,"props":2812,"children":2813},{"style":144},[2814],{"type":51,"value":2514},{"type":46,"tag":102,"props":2816,"children":2817},{"style":144},[2818],{"type":51,"value":2819}," {}",{"type":46,"tag":102,"props":2821,"children":2822},{"style":109},[2823],{"type":51,"value":2824},"  # type: DATE, auto-generates timeframes\n",{"type":46,"tag":102,"props":2826,"children":2827},{"class":104,"line":221},[2828,2833,2837,2841],{"type":46,"tag":102,"props":2829,"children":2830},{"style":2481},[2831],{"type":51,"value":2832},"  revenue",{"type":46,"tag":102,"props":2834,"children":2835},{"style":144},[2836],{"type":51,"value":2514},{"type":46,"tag":102,"props":2838,"children":2839},{"style":144},[2840],{"type":51,"value":2819},{"type":46,"tag":102,"props":2842,"children":2843},{"style":109},[2844],{"type":51,"value":2845},"     # type: NUMERIC\n",{"type":46,"tag":102,"props":2847,"children":2848},{"class":104,"line":259},[2849],{"type":46,"tag":102,"props":2850,"children":2851},{"emptyLinePlaceholder":263},[2852],{"type":51,"value":266},{"type":46,"tag":102,"props":2854,"children":2855},{"class":104,"line":269},[2856],{"type":46,"tag":102,"props":2857,"children":2858},{"style":109},[2859],{"type":51,"value":2860},"# Extension layer (your YAML)\n",{"type":46,"tag":102,"props":2862,"children":2863},{"class":104,"line":278},[2864,2868],{"type":46,"tag":102,"props":2865,"children":2866},{"style":2481},[2867],{"type":51,"value":2484},{"type":46,"tag":102,"props":2869,"children":2870},{"style":144},[2871],{"type":51,"value":2489},{"type":46,"tag":102,"props":2873,"children":2874},{"class":104,"line":1492},[2875,2879],{"type":46,"tag":102,"props":2876,"children":2877},{"style":2481},[2878],{"type":51,"value":2557},{"type":46,"tag":102,"props":2880,"children":2881},{"style":144},[2882],{"type":51,"value":2489},{"type":46,"tag":102,"props":2884,"children":2885},{"class":104,"line":2072},[2886,2890,2894,2898,2903],{"type":46,"tag":102,"props":2887,"children":2888},{"style":2481},[2889],{"type":51,"value":2540},{"type":46,"tag":102,"props":2891,"children":2892},{"style":144},[2893],{"type":51,"value":2514},{"type":46,"tag":102,"props":2895,"children":2896},{"style":144},[2897],{"type":51,"value":167},{"type":46,"tag":102,"props":2899,"children":2900},{"style":133},[2901],{"type":51,"value":2902},"Order Created",{"type":46,"tag":102,"props":2904,"children":2905},{"style":144},[2906],{"type":51,"value":177},{"type":46,"tag":102,"props":2908,"children":2909},{"class":104,"line":2604},[2910,2915,2919,2923,2928],{"type":46,"tag":102,"props":2911,"children":2912},{"style":2481},[2913],{"type":51,"value":2914},"    description",{"type":46,"tag":102,"props":2916,"children":2917},{"style":144},[2918],{"type":51,"value":2514},{"type":46,"tag":102,"props":2920,"children":2921},{"style":144},[2922],{"type":51,"value":167},{"type":46,"tag":102,"props":2924,"children":2925},{"style":133},[2926],{"type":51,"value":2927},"When the order was placed",{"type":46,"tag":102,"props":2929,"children":2930},{"style":144},[2931],{"type":51,"value":177},{"type":46,"tag":102,"props":2933,"children":2934},{"class":104,"line":2622},[2935],{"type":46,"tag":102,"props":2936,"children":2937},{"emptyLinePlaceholder":263},[2938],{"type":51,"value":266},{"type":46,"tag":102,"props":2940,"children":2941},{"class":104,"line":2635},[2942,2946],{"type":46,"tag":102,"props":2943,"children":2944},{"style":2481},[2945],{"type":51,"value":2832},{"type":46,"tag":102,"props":2947,"children":2948},{"style":144},[2949],{"type":51,"value":2489},{"type":46,"tag":102,"props":2951,"children":2952},{"class":104,"line":2653},[2953,2958,2962,2967],{"type":46,"tag":102,"props":2954,"children":2955},{"style":2481},[2956],{"type":51,"value":2957},"    hidden",{"type":46,"tag":102,"props":2959,"children":2960},{"style":144},[2961],{"type":51,"value":2514},{"type":46,"tag":102,"props":2963,"children":2964},{"style":2517},[2965],{"type":51,"value":2966}," true",{"type":46,"tag":102,"props":2968,"children":2969},{"style":109},[2970],{"type":51,"value":2971},"  # Hide the raw column\n",{"type":46,"tag":102,"props":2973,"children":2974},{"class":104,"line":2670},[2975],{"type":46,"tag":102,"props":2976,"children":2977},{"emptyLinePlaceholder":263},[2978],{"type":51,"value":266},{"type":46,"tag":102,"props":2980,"children":2982},{"class":104,"line":2981},15,[2983,2987],{"type":46,"tag":102,"props":2984,"children":2985},{"style":2481},[2986],{"type":51,"value":2585},{"type":46,"tag":102,"props":2988,"children":2989},{"style":144},[2990],{"type":51,"value":2489},{"type":46,"tag":102,"props":2992,"children":2994},{"class":104,"line":2993},16,[2995,2999],{"type":46,"tag":102,"props":2996,"children":2997},{"style":2481},[2998],{"type":51,"value":2628},{"type":46,"tag":102,"props":3000,"children":3001},{"style":144},[3002],{"type":51,"value":2489},{"type":46,"tag":102,"props":3004,"children":3006},{"class":104,"line":3005},17,[3007,3011,3015],{"type":46,"tag":102,"props":3008,"children":3009},{"style":2481},[3010],{"type":51,"value":2641},{"type":46,"tag":102,"props":3012,"children":3013},{"style":144},[3014],{"type":51,"value":2514},{"type":46,"tag":102,"props":3016,"children":3017},{"style":133},[3018],{"type":51,"value":3019}," ${revenue}\n",{"type":46,"tag":102,"props":3021,"children":3023},{"class":104,"line":3022},18,[3024,3028,3032],{"type":46,"tag":102,"props":3025,"children":3026},{"style":2481},[3027],{"type":51,"value":2610},{"type":46,"tag":102,"props":3029,"children":3030},{"style":144},[3031],{"type":51,"value":2514},{"type":46,"tag":102,"props":3033,"children":3034},{"style":133},[3035],{"type":51,"value":2667},{"type":46,"tag":102,"props":3037,"children":3039},{"class":104,"line":3038},19,[3040,3044,3048],{"type":46,"tag":102,"props":3041,"children":3042},{"style":2481},[3043],{"type":51,"value":2676},{"type":46,"tag":102,"props":3045,"children":3046},{"style":144},[3047],{"type":51,"value":2514},{"type":46,"tag":102,"props":3049,"children":3050},{"style":133},[3051],{"type":51,"value":2685},{"type":46,"tag":54,"props":3053,"children":3054},{},[3055,3057,3062,3064,3069,3071,3077],{"type":51,"value":3056},"Result: ",{"type":46,"tag":75,"props":3058,"children":3060},{"className":3059},[],[3061],{"type":51,"value":2767},{"type":51,"value":3063}," inherits its type from the schema layer (DATE with automatic week\u002Fmonth\u002Fyear granularities) but gets your label. The raw ",{"type":46,"tag":75,"props":3065,"children":3067},{"className":3066},[],[3068],{"type":51,"value":2775},{"type":51,"value":3070}," column is hidden, only exposed through the ",{"type":46,"tag":75,"props":3072,"children":3074},{"className":3073},[],[3075],{"type":51,"value":3076},"total_revenue",{"type":51,"value":3078}," measure.",{"type":46,"tag":54,"props":3080,"children":3081},{},[3082,3087],{"type":46,"tag":67,"props":3083,"children":3084},{},[3085],{"type":51,"value":3086},"Key insight",{"type":51,"value":3088},": If your extension defines a dimension but there's no schema layer base dimension to provide type information, Omni can't infer granularities or types. Trigger a schema refresh to auto-generate the schema layer first.",{"type":46,"tag":54,"props":3090,"children":3091},{},[3092,3104,3105,3110,3112,3117,3119,3124,3126,3131,3133,3138],{"type":46,"tag":67,"props":3093,"children":3094},{},[3095,3097,3103],{"type":51,"value":3096},"Reading back what you wrote — ",{"type":46,"tag":75,"props":3098,"children":3100},{"className":3099},[],[3101],{"type":51,"value":3102},"--mode",{"type":51,"value":386},{"type":51,"value":1254},{"type":46,"tag":75,"props":3106,"children":3108},{"className":3107},[],[3109],{"type":51,"value":1201},{"type":51,"value":3111}," returns your ",{"type":46,"tag":67,"props":3113,"children":3114},{},[3115],{"type":51,"value":3116},"extension",{"type":51,"value":3118}," layer by default — just the deltas you authored, ",{"type":46,"tag":353,"props":3120,"children":3121},{},[3122],{"type":51,"value":3123},"not",{"type":51,"value":3125}," the auto-generated base columns. To see the ",{"type":46,"tag":67,"props":3127,"children":3128},{},[3129],{"type":51,"value":3130},"fully-composed",{"type":51,"value":3132}," result (schema base + your extension merged), read with ",{"type":46,"tag":75,"props":3134,"children":3136},{"className":3135},[],[3137],{"type":51,"value":1336},{"type":51,"value":2514},{"type":46,"tag":91,"props":3140,"children":3142},{"className":93,"code":3141,"language":95,"meta":96,"style":96},"# What you authored (deltas only) — default\nomni models yaml-get \u003CmodelId> --filename your_view.view --branchid \u003CbranchId> --mode extension\n\n# What the model actually resolves to (schema + extension merged)\nomni models yaml-get \u003CmodelId> --filename your_view.view --branchid \u003CbranchId> --mode combined\n",[3143],{"type":46,"tag":75,"props":3144,"children":3145},{"__ignoreMap":96},[3146,3154,3223,3230,3238],{"type":46,"tag":102,"props":3147,"children":3148},{"class":104,"line":105},[3149],{"type":46,"tag":102,"props":3150,"children":3151},{"style":109},[3152],{"type":51,"value":3153},"# What you authored (deltas only) — default\n",{"type":46,"tag":102,"props":3155,"children":3156},{"class":104,"line":115},[3157,3161,3165,3169,3173,3177,3181,3185,3189,3193,3197,3201,3205,3209,3213,3218],{"type":46,"tag":102,"props":3158,"children":3159},{"style":198},[3160],{"type":51,"value":15},{"type":46,"tag":102,"props":3162,"children":3163},{"style":133},[3164],{"type":51,"value":538},{"type":46,"tag":102,"props":3166,"children":3167},{"style":133},[3168],{"type":51,"value":1506},{"type":46,"tag":102,"props":3170,"children":3171},{"style":144},[3172],{"type":51,"value":240},{"type":46,"tag":102,"props":3174,"children":3175},{"style":133},[3176],{"type":51,"value":836},{"type":46,"tag":102,"props":3178,"children":3179},{"style":248},[3180],{"type":51,"value":841},{"type":46,"tag":102,"props":3182,"children":3183},{"style":144},[3184],{"type":51,"value":846},{"type":46,"tag":102,"props":3186,"children":3187},{"style":133},[3188],{"type":51,"value":1527},{"type":46,"tag":102,"props":3190,"children":3191},{"style":133},[3192],{"type":51,"value":1532},{"type":46,"tag":102,"props":3194,"children":3195},{"style":133},[3196],{"type":51,"value":1408},{"type":46,"tag":102,"props":3198,"children":3199},{"style":144},[3200],{"type":51,"value":240},{"type":46,"tag":102,"props":3202,"children":3203},{"style":133},[3204],{"type":51,"value":1417},{"type":46,"tag":102,"props":3206,"children":3207},{"style":248},[3208],{"type":51,"value":841},{"type":46,"tag":102,"props":3210,"children":3211},{"style":144},[3212],{"type":51,"value":846},{"type":46,"tag":102,"props":3214,"children":3215},{"style":133},[3216],{"type":51,"value":3217}," --mode",{"type":46,"tag":102,"props":3219,"children":3220},{"style":133},[3221],{"type":51,"value":3222}," extension\n",{"type":46,"tag":102,"props":3224,"children":3225},{"class":104,"line":33},[3226],{"type":46,"tag":102,"props":3227,"children":3228},{"emptyLinePlaceholder":263},[3229],{"type":51,"value":266},{"type":46,"tag":102,"props":3231,"children":3232},{"class":104,"line":221},[3233],{"type":46,"tag":102,"props":3234,"children":3235},{"style":109},[3236],{"type":51,"value":3237},"# What the model actually resolves to (schema + extension merged)\n",{"type":46,"tag":102,"props":3239,"children":3240},{"class":104,"line":259},[3241,3245,3249,3253,3257,3261,3265,3269,3273,3277,3281,3285,3289,3293,3297,3301],{"type":46,"tag":102,"props":3242,"children":3243},{"style":198},[3244],{"type":51,"value":15},{"type":46,"tag":102,"props":3246,"children":3247},{"style":133},[3248],{"type":51,"value":538},{"type":46,"tag":102,"props":3250,"children":3251},{"style":133},[3252],{"type":51,"value":1506},{"type":46,"tag":102,"props":3254,"children":3255},{"style":144},[3256],{"type":51,"value":240},{"type":46,"tag":102,"props":3258,"children":3259},{"style":133},[3260],{"type":51,"value":836},{"type":46,"tag":102,"props":3262,"children":3263},{"style":248},[3264],{"type":51,"value":841},{"type":46,"tag":102,"props":3266,"children":3267},{"style":144},[3268],{"type":51,"value":846},{"type":46,"tag":102,"props":3270,"children":3271},{"style":133},[3272],{"type":51,"value":1527},{"type":46,"tag":102,"props":3274,"children":3275},{"style":133},[3276],{"type":51,"value":1532},{"type":46,"tag":102,"props":3278,"children":3279},{"style":133},[3280],{"type":51,"value":1408},{"type":46,"tag":102,"props":3282,"children":3283},{"style":144},[3284],{"type":51,"value":240},{"type":46,"tag":102,"props":3286,"children":3287},{"style":133},[3288],{"type":51,"value":1417},{"type":46,"tag":102,"props":3290,"children":3291},{"style":248},[3292],{"type":51,"value":841},{"type":46,"tag":102,"props":3294,"children":3295},{"style":144},[3296],{"type":51,"value":846},{"type":46,"tag":102,"props":3298,"children":3299},{"style":133},[3300],{"type":51,"value":3217},{"type":46,"tag":102,"props":3302,"children":3303},{"style":133},[3304],{"type":51,"value":3305}," combined\n",{"type":46,"tag":54,"props":3307,"children":3308},{},[3309,3311,3316,3318,3323,3325,3331,3332,3337,3339,3343,3345,3351,3353,3359,3361,3366,3368,3374,3375,3381,3382,3388],{"type":51,"value":3310},"Use ",{"type":46,"tag":75,"props":3312,"children":3314},{"className":3313},[],[3315],{"type":51,"value":3116},{"type":51,"value":3317}," to confirm ",{"type":46,"tag":353,"props":3319,"children":3320},{},[3321],{"type":51,"value":3322},"what you changed",{"type":51,"value":3324},", and ",{"type":46,"tag":75,"props":3326,"children":3328},{"className":3327},[],[3329],{"type":51,"value":3330},"combined",{"type":51,"value":3317},{"type":46,"tag":353,"props":3333,"children":3334},{},[3335],{"type":51,"value":3336},"what the model resolves to",{"type":51,"value":3338},". When the model is git-integrated, the ",{"type":46,"tag":67,"props":3340,"children":3341},{},[3342],{"type":51,"value":3330},{"type":51,"value":3344}," output mirrors what's written to the repository — which is why committed ",{"type":46,"tag":75,"props":3346,"children":3348},{"className":3347},[],[3349],{"type":51,"value":3350},"*.view.yaml",{"type":51,"value":3352}," files carry the schema-layer ",{"type":46,"tag":75,"props":3354,"children":3356},{"className":3355},[],[3357],{"type":51,"value":3358},"table_name:",{"type":51,"value":3360}," and base columns, while the extension layer holds only your deltas. (Other ",{"type":46,"tag":75,"props":3362,"children":3364},{"className":3363},[],[3365],{"type":51,"value":3102},{"type":51,"value":3367}," values: ",{"type":46,"tag":75,"props":3369,"children":3371},{"className":3370},[],[3372],{"type":51,"value":3373},"staged",{"type":51,"value":1785},{"type":46,"tag":75,"props":3376,"children":3378},{"className":3377},[],[3379],{"type":51,"value":3380},"merged",{"type":51,"value":1785},{"type":46,"tag":75,"props":3383,"children":3385},{"className":3384},[],[3386],{"type":51,"value":3387},"history",{"type":51,"value":3389},".)",{"type":46,"tag":799,"props":3391,"children":3393},{"id":3392},"dimension-parameters",[3394],{"type":51,"value":3395},"Dimension Parameters",{"type":46,"tag":54,"props":3397,"children":3398},{},[3399,3400,3406],{"type":51,"value":656},{"type":46,"tag":75,"props":3401,"children":3403},{"className":3402},[],[3404],{"type":51,"value":3405},"references\u002FmodelParameters.md",{"type":51,"value":3407}," for the complete list of 35+ dimension parameters, format values, and timeframes.",{"type":46,"tag":54,"props":3409,"children":3410},{},[3411],{"type":51,"value":3412},"Most common parameters:",{"type":46,"tag":672,"props":3414,"children":3415},{},[3416,3482,3501,3511,3522,3559,3570],{"type":46,"tag":444,"props":3417,"children":3418},{},[3419,3425,3427,3433,3435,3441,3442,3448,3450,3456,3458,3464,3466,3472,3474,3480],{"type":46,"tag":75,"props":3420,"children":3422},{"className":3421},[],[3423],{"type":51,"value":3424},"sql",{"type":51,"value":3426}," — SQL expression using ",{"type":46,"tag":75,"props":3428,"children":3430},{"className":3429},[],[3431],{"type":51,"value":3432},"${field_name}",{"type":51,"value":3434}," references. Reference other fields\nwith ",{"type":46,"tag":75,"props":3436,"children":3438},{"className":3437},[],[3439],{"type":51,"value":3440},"${field}",{"type":51,"value":1629},{"type":46,"tag":75,"props":3443,"children":3445},{"className":3444},[],[3446],{"type":51,"value":3447},"${view.field}",{"type":51,"value":3449},"; a raw column auto-maps by name (no ",{"type":46,"tag":75,"props":3451,"children":3453},{"className":3452},[],[3454],{"type":51,"value":3455},"sql:",{"type":51,"value":3457},").\nThere is no ",{"type":46,"tag":75,"props":3459,"children":3461},{"className":3460},[],[3462],{"type":51,"value":3463},"${TABLE}",{"type":51,"value":3465}," construct — ",{"type":46,"tag":75,"props":3467,"children":3469},{"className":3468},[],[3470],{"type":51,"value":3471},"${TABLE}.column",{"type":51,"value":3473}," errors with\n",{"type":46,"tag":75,"props":3475,"children":3477},{"className":3476},[],[3478],{"type":51,"value":3479},"Column \"__omni_scoped\" not found",{"type":51,"value":3481}," at validation and query time.",{"type":46,"tag":444,"props":3483,"children":3484},{},[3485,3491,3493,3499],{"type":46,"tag":75,"props":3486,"children":3488},{"className":3487},[],[3489],{"type":51,"value":3490},"label",{"type":51,"value":3492}," — display name · ",{"type":46,"tag":75,"props":3494,"children":3496},{"className":3495},[],[3497],{"type":51,"value":3498},"description",{"type":51,"value":3500}," — help text (also used by Blobby)",{"type":46,"tag":444,"props":3502,"children":3503},{},[3504,3509],{"type":46,"tag":75,"props":3505,"children":3507},{"className":3506},[],[3508],{"type":51,"value":2296},{"type":51,"value":3510}," — unique key (critical for aggregations)",{"type":46,"tag":444,"props":3512,"children":3513},{},[3514,3520],{"type":46,"tag":75,"props":3515,"children":3517},{"className":3516},[],[3518],{"type":51,"value":3519},"hidden: true",{"type":51,"value":3521}," — hides from picker, still usable in SQL",{"type":46,"tag":444,"props":3523,"children":3524},{},[3525,3531,3533,3539,3540,3546,3547,3553,3554],{"type":46,"tag":75,"props":3526,"children":3528},{"className":3527},[],[3529],{"type":51,"value":3530},"format",{"type":51,"value":3532}," — ",{"type":46,"tag":75,"props":3534,"children":3536},{"className":3535},[],[3537],{"type":51,"value":3538},"number_2",{"type":51,"value":1785},{"type":46,"tag":75,"props":3541,"children":3543},{"className":3542},[],[3544],{"type":51,"value":3545},"currency_2",{"type":51,"value":1785},{"type":46,"tag":75,"props":3548,"children":3550},{"className":3549},[],[3551],{"type":51,"value":3552},"percent_2",{"type":51,"value":1785},{"type":46,"tag":75,"props":3555,"children":3557},{"className":3556},[],[3558],{"type":51,"value":2306},{"type":46,"tag":444,"props":3560,"children":3561},{},[3562,3568],{"type":46,"tag":75,"props":3563,"children":3565},{"className":3564},[],[3566],{"type":51,"value":3567},"group_label",{"type":51,"value":3569}," — groups fields in the picker",{"type":46,"tag":444,"props":3571,"children":3572},{},[3573,3579,3581,3587],{"type":46,"tag":75,"props":3574,"children":3576},{"className":3575},[],[3577],{"type":51,"value":3578},"synonyms",{"type":51,"value":3580}," — alternative names for AI matching (e.g., ",{"type":46,"tag":75,"props":3582,"children":3584},{"className":3583},[],[3585],{"type":51,"value":3586},"[client, account, buyer]",{"type":51,"value":3588},")",{"type":46,"tag":799,"props":3590,"children":3592},{"id":3591},"measure-parameters",[3593],{"type":51,"value":3594},"Measure Parameters",{"type":46,"tag":54,"props":3596,"children":3597},{},[3598,3599,3604],{"type":51,"value":656},{"type":46,"tag":75,"props":3600,"children":3602},{"className":3601},[],[3603],{"type":51,"value":3405},{"type":51,"value":3605}," for the complete list of 24+ measure parameters and all 13 aggregate types.",{"type":46,"tag":54,"props":3607,"children":3608},{},[3609,3611,3617],{"type":51,"value":3610},"Measure filters restrict rows before aggregation using the YAML filter condition syntax. See ",{"type":46,"tag":75,"props":3612,"children":3614},{"className":3613},[],[3615],{"type":51,"value":3616},"references\u002Fyaml-filter-syntax.md",{"type":51,"value":3618}," for the complete operator reference and measure filter examples.",{"type":46,"tag":54,"props":3620,"children":3621},{},[3622,3624,3630,3632,3637,3639,3645,3647,3653,3655,3660],{"type":51,"value":3623},"Prefer a measure ",{"type":46,"tag":75,"props":3625,"children":3627},{"className":3626},[],[3628],{"type":51,"value":3629},"filters:",{"type":51,"value":3631}," block for filtered aggregates instead of embedding filter logic in ",{"type":46,"tag":75,"props":3633,"children":3635},{"className":3634},[],[3636],{"type":51,"value":3424},{"type":51,"value":3638}," with ",{"type":46,"tag":75,"props":3640,"children":3642},{"className":3641},[],[3643],{"type":51,"value":3644},"CASE WHEN",{"type":51,"value":3646}," or a SQL ",{"type":46,"tag":75,"props":3648,"children":3650},{"className":3649},[],[3651],{"type":51,"value":3652},"WHERE",{"type":51,"value":3654}," clause. Keep ",{"type":46,"tag":75,"props":3656,"children":3658},{"className":3657},[],[3659],{"type":51,"value":3424},{"type":51,"value":3661}," focused on the value being aggregated:",{"type":46,"tag":91,"props":3663,"children":3665},{"className":2470,"code":3664,"language":19,"meta":96,"style":96},"measures:\n  completed_revenue:\n    sql: ${sale_price}\n    aggregate_type: sum\n    filters:\n      status:\n        is: Complete\n",[3666],{"type":46,"tag":75,"props":3667,"children":3668},{"__ignoreMap":96},[3669,3680,3692,3707,3722,3734,3746],{"type":46,"tag":102,"props":3670,"children":3671},{"class":104,"line":105},[3672,3676],{"type":46,"tag":102,"props":3673,"children":3674},{"style":2481},[3675],{"type":51,"value":2585},{"type":46,"tag":102,"props":3677,"children":3678},{"style":144},[3679],{"type":51,"value":2489},{"type":46,"tag":102,"props":3681,"children":3682},{"class":104,"line":115},[3683,3688],{"type":46,"tag":102,"props":3684,"children":3685},{"style":2481},[3686],{"type":51,"value":3687},"  completed_revenue",{"type":46,"tag":102,"props":3689,"children":3690},{"style":144},[3691],{"type":51,"value":2489},{"type":46,"tag":102,"props":3693,"children":3694},{"class":104,"line":33},[3695,3699,3703],{"type":46,"tag":102,"props":3696,"children":3697},{"style":2481},[3698],{"type":51,"value":2641},{"type":46,"tag":102,"props":3700,"children":3701},{"style":144},[3702],{"type":51,"value":2514},{"type":46,"tag":102,"props":3704,"children":3705},{"style":133},[3706],{"type":51,"value":2650},{"type":46,"tag":102,"props":3708,"children":3709},{"class":104,"line":221},[3710,3714,3718],{"type":46,"tag":102,"props":3711,"children":3712},{"style":2481},[3713],{"type":51,"value":2610},{"type":46,"tag":102,"props":3715,"children":3716},{"style":144},[3717],{"type":51,"value":2514},{"type":46,"tag":102,"props":3719,"children":3720},{"style":133},[3721],{"type":51,"value":2667},{"type":46,"tag":102,"props":3723,"children":3724},{"class":104,"line":259},[3725,3730],{"type":46,"tag":102,"props":3726,"children":3727},{"style":2481},[3728],{"type":51,"value":3729},"    filters",{"type":46,"tag":102,"props":3731,"children":3732},{"style":144},[3733],{"type":51,"value":2489},{"type":46,"tag":102,"props":3735,"children":3736},{"class":104,"line":269},[3737,3742],{"type":46,"tag":102,"props":3738,"children":3739},{"style":2481},[3740],{"type":51,"value":3741},"      status",{"type":46,"tag":102,"props":3743,"children":3744},{"style":144},[3745],{"type":51,"value":2489},{"type":46,"tag":102,"props":3747,"children":3748},{"class":104,"line":278},[3749,3754,3758],{"type":46,"tag":102,"props":3750,"children":3751},{"style":2481},[3752],{"type":51,"value":3753},"        is",{"type":46,"tag":102,"props":3755,"children":3756},{"style":144},[3757],{"type":51,"value":2514},{"type":46,"tag":102,"props":3759,"children":3760},{"style":133},[3761],{"type":51,"value":3762}," Complete\n",{"type":46,"tag":799,"props":3764,"children":3766},{"id":3765},"cross-view-fields-in-views",[3767],{"type":51,"value":3768},"Cross-View Fields in Views",{"type":46,"tag":54,"props":3770,"children":3771},{},[3772,3774,3779,3781,3787],{"type":51,"value":3773},"Avoid defining cross-view fields (dimensions or measures whose ",{"type":46,"tag":75,"props":3775,"children":3777},{"className":3776},[],[3778],{"type":51,"value":3424},{"type":51,"value":3780}," references ",{"type":46,"tag":75,"props":3782,"children":3784},{"className":3783},[],[3785],{"type":51,"value":3786},"${other_view.field}",{"type":51,"value":3788},") directly in a view file. These fields depend on another view being joined, which is not guaranteed in every topic that includes this view. In topics where the referenced view isn't present, the field will be omitted — but more importantly, the model validator will throw errors for any topic that includes this view without also joining the referenced view. This can create a cascade of validator errors across topics that are otherwise valid but happen to include only a subset of the involved views.",{"type":46,"tag":54,"props":3790,"children":3791},{},[3792,3805],{"type":46,"tag":67,"props":3793,"children":3794},{},[3795,3797,3803],{"type":51,"value":3796},"In the vast majority of cases, cross-view fields should be defined in the topic's ",{"type":46,"tag":75,"props":3798,"children":3800},{"className":3799},[],[3801],{"type":51,"value":3802},"views:",{"type":51,"value":3804}," block",{"type":51,"value":3806}," (see \"Topic-Scoped View Definitions\"), where the join context is explicit and controlled.",{"type":46,"tag":54,"props":3808,"children":3809},{},[3810],{"type":51,"value":3811},"Only define a cross-view field in the view file itself when you are certain the referenced view will always be joined in every topic that includes this view — for example, when the join is defined globally and the two views are inseparable by design.",{"type":46,"tag":84,"props":3813,"children":3815},{"id":3814},"fallback-view-missing-from-yaml-get",[3816],{"type":51,"value":3817},"Fallback: View Missing from yaml-get",{"type":46,"tag":54,"props":3819,"children":3820},{},[3821,3823,3828],{"type":51,"value":3822},"Before concluding that a view doesn't exist, always run this two-step check. ",{"type":46,"tag":75,"props":3824,"children":3826},{"className":3825},[],[3827],{"type":51,"value":1201},{"type":51,"value":3829}," only returns views from currently-loaded schemas — views in offloaded or inactive schemas won't appear, but they're still available.",{"type":46,"tag":91,"props":3831,"children":3833},{"className":93,"code":3832,"language":95,"meta":96,"style":96},"# 1. List all schemas the connection knows about (loaded, offloaded, and inactive)\nomni models get-schemas \u003CmodelId>\n# → {\"schemas\": [\"ANALYTICS\", \"PUBLIC\", \"STAGING\", ...]}\n\n# 2. If the target schema appears in the list, load it explicitly\nomni models yaml-get \u003CmodelId> --includeschemas PUBLIC\n",[3834],{"type":46,"tag":75,"props":3835,"children":3836},{"__ignoreMap":96},[3837,3845,3877,3885,3892,3900],{"type":46,"tag":102,"props":3838,"children":3839},{"class":104,"line":105},[3840],{"type":46,"tag":102,"props":3841,"children":3842},{"style":109},[3843],{"type":51,"value":3844},"# 1. List all schemas the connection knows about (loaded, offloaded, and inactive)\n",{"type":46,"tag":102,"props":3846,"children":3847},{"class":104,"line":115},[3848,3852,3856,3861,3865,3869,3873],{"type":46,"tag":102,"props":3849,"children":3850},{"style":198},[3851],{"type":51,"value":15},{"type":46,"tag":102,"props":3853,"children":3854},{"style":133},[3855],{"type":51,"value":538},{"type":46,"tag":102,"props":3857,"children":3858},{"style":133},[3859],{"type":51,"value":3860}," get-schemas",{"type":46,"tag":102,"props":3862,"children":3863},{"style":144},[3864],{"type":51,"value":240},{"type":46,"tag":102,"props":3866,"children":3867},{"style":133},[3868],{"type":51,"value":836},{"type":46,"tag":102,"props":3870,"children":3871},{"style":248},[3872],{"type":51,"value":841},{"type":46,"tag":102,"props":3874,"children":3875},{"style":144},[3876],{"type":51,"value":256},{"type":46,"tag":102,"props":3878,"children":3879},{"class":104,"line":33},[3880],{"type":46,"tag":102,"props":3881,"children":3882},{"style":109},[3883],{"type":51,"value":3884},"# → {\"schemas\": [\"ANALYTICS\", \"PUBLIC\", \"STAGING\", ...]}\n",{"type":46,"tag":102,"props":3886,"children":3887},{"class":104,"line":221},[3888],{"type":46,"tag":102,"props":3889,"children":3890},{"emptyLinePlaceholder":263},[3891],{"type":51,"value":266},{"type":46,"tag":102,"props":3893,"children":3894},{"class":104,"line":259},[3895],{"type":46,"tag":102,"props":3896,"children":3897},{"style":109},[3898],{"type":51,"value":3899},"# 2. If the target schema appears in the list, load it explicitly\n",{"type":46,"tag":102,"props":3901,"children":3902},{"class":104,"line":269},[3903,3907,3911,3915,3919,3923,3927,3931,3936],{"type":46,"tag":102,"props":3904,"children":3905},{"style":198},[3906],{"type":51,"value":15},{"type":46,"tag":102,"props":3908,"children":3909},{"style":133},[3910],{"type":51,"value":538},{"type":46,"tag":102,"props":3912,"children":3913},{"style":133},[3914],{"type":51,"value":1506},{"type":46,"tag":102,"props":3916,"children":3917},{"style":144},[3918],{"type":51,"value":240},{"type":46,"tag":102,"props":3920,"children":3921},{"style":133},[3922],{"type":51,"value":836},{"type":46,"tag":102,"props":3924,"children":3925},{"style":248},[3926],{"type":51,"value":841},{"type":46,"tag":102,"props":3928,"children":3929},{"style":144},[3930],{"type":51,"value":846},{"type":46,"tag":102,"props":3932,"children":3933},{"style":133},[3934],{"type":51,"value":3935}," --includeschemas",{"type":46,"tag":102,"props":3937,"children":3938},{"style":133},[3939],{"type":51,"value":3940}," PUBLIC\n",{"type":46,"tag":54,"props":3942,"children":3943},{},[3944],{"type":46,"tag":67,"props":3945,"children":3946},{},[3947,3949,3955],{"type":51,"value":3948},"Rules for ",{"type":46,"tag":75,"props":3950,"children":3952},{"className":3951},[],[3953],{"type":51,"value":3954},"--includeschemas",{"type":51,"value":2514},{"type":46,"tag":672,"props":3957,"children":3958},{},[3959,3971,3976],{"type":46,"tag":444,"props":3960,"children":3961},{},[3962,3964,3969],{"type":51,"value":3963},"Accepts exactly ",{"type":46,"tag":67,"props":3965,"children":3966},{},[3967],{"type":51,"value":3968},"one schema name",{"type":51,"value":3970}," per call — commas are rejected. Load schemas one at a time.",{"type":46,"tag":444,"props":3972,"children":3973},{},[3974],{"type":51,"value":3975},"The response will contain only views from that schema; relationships to other schemas are preserved.",{"type":46,"tag":444,"props":3977,"children":3978},{},[3979,3981,3987,3989,3994,3995,4001,4002,4008],{"type":51,"value":3980},"To scope to a branch, add ",{"type":46,"tag":75,"props":3982,"children":3984},{"className":3983},[],[3985],{"type":51,"value":3986},"--branchid \u003Cid>",{"type":51,"value":3988}," to ",{"type":46,"tag":75,"props":3990,"children":3992},{"className":3991},[],[3993],{"type":51,"value":1201},{"type":51,"value":313},{"type":46,"tag":75,"props":3996,"children":3998},{"className":3997},[],[3999],{"type":51,"value":4000},"--branch-id \u003Cid>",{"type":51,"value":3988},{"type":46,"tag":75,"props":4003,"children":4005},{"className":4004},[],[4006],{"type":51,"value":4007},"get-schemas",{"type":51,"value":4009}," (flag names differ per command).",{"type":46,"tag":54,"props":4011,"children":4012},{},[4013,4015,4020],{"type":51,"value":4014},"If the schema isn't in the ",{"type":46,"tag":75,"props":4016,"children":4018},{"className":4017},[],[4019],{"type":51,"value":4007},{"type":51,"value":4021}," list at all, the connection likely doesn't have access or the schema isn't synced — check with a Connection Admin.",{"type":46,"tag":84,"props":4023,"children":4025},{"id":4024},"writing-topics",[4026],{"type":51,"value":4027},"Writing Topics",{"type":46,"tag":60,"props":4029,"children":4030},{},[4031],{"type":46,"tag":54,"props":4032,"children":4033},{},[4034,4039,4041,4047],{"type":46,"tag":67,"props":4035,"children":4036},{},[4037],{"type":51,"value":4038},"Before writing a topic, verify all views you plan to reference actually exist.",{"type":51,"value":4040}," Run ",{"type":46,"tag":75,"props":4042,"children":4044},{"className":4043},[],[4045],{"type":51,"value":4046},"omni models yaml-get \u003CmodelId>",{"type":51,"value":4048}," and confirm each view appears. If a view is missing, run the lazy-load fallback above before concluding it doesn't exist — it may simply be in an offloaded schema.",{"type":46,"tag":799,"props":4050,"children":4052},{"id":4051},"new-topic-vs-extend-an-existing-one",[4053],{"type":51,"value":4054},"New topic vs extend an existing one",{"type":46,"tag":54,"props":4056,"children":4057},{},[4058,4060,4065,4067,4072],{"type":51,"value":4059},"When a query can't be answered by an existing topic, first check whether you should simply ",{"type":46,"tag":67,"props":4061,"children":4062},{},[4063],{"type":51,"value":4064},"extend",{"type":51,"value":4066}," one rather than create a new one. Extending is usually right when the request's base view (the FROM) matches an existing topic's base view — e.g. add a relationship\u002Fjoin so a needed view becomes reachable, or add a field\u002Flabel. ",{"type":46,"tag":67,"props":4068,"children":4069},{},[4070],{"type":51,"value":4071},"Create a new topic",{"type":51,"value":4073}," when any of these is fundamentally different:",{"type":46,"tag":672,"props":4075,"children":4076},{},[4077,4094,4119],{"type":46,"tag":444,"props":4078,"children":4079},{},[4080,4085,4087,4092],{"type":46,"tag":67,"props":4081,"children":4082},{},[4083],{"type":51,"value":4084},"Subject \u002F object",{"type":51,"value":4086}," — a different base view (FROM). What is the query fundamentally describing — orders? users? ",{"type":46,"tag":353,"props":4088,"children":4089},{},[4090],{"type":51,"value":4091},"time",{"type":51,"value":4093}," (dates)? A different core entity warrants its own topic.",{"type":46,"tag":444,"props":4095,"children":4096},{},[4097,4102,4104,4109,4111,4117],{"type":46,"tag":67,"props":4098,"children":4099},{},[4100],{"type":51,"value":4101},"Constraints",{"type":51,"value":4103}," — conditions that are ",{"type":46,"tag":353,"props":4105,"children":4106},{},[4107],{"type":51,"value":4108},"always",{"type":51,"value":4110}," applied (e.g. an ",{"type":46,"tag":75,"props":4112,"children":4114},{"className":4113},[],[4115],{"type":51,"value":4116},"always_where",{"type":51,"value":4118}," that excludes test users from order data). Different always-on filters → a different topic.",{"type":46,"tag":444,"props":4120,"children":4121},{},[4122,4127],{"type":46,"tag":67,"props":4123,"children":4124},{},[4125],{"type":51,"value":4126},"Audience",{"type":51,"value":4128}," — the same fields but different labels\u002Fterminology for a different consumer; jargon that differs by audience justifies a separate topic.",{"type":46,"tag":54,"props":4130,"children":4131},{},[4132],{"type":51,"value":4133},"Prompt the requestor when it's a judgment call, and build new topics on a branch (see the Safe Development Workflow above). Note that querying on a topic (vs a bare base view) is also what makes the result accessible to restricted queriers\u002Fviewers.",{"type":46,"tag":54,"props":4135,"children":4136},{},[4137,4138,4145,4147,4154],{"type":51,"value":656},{"type":46,"tag":2380,"props":4139,"children":4142},{"href":4140,"rel":4141},"https:\u002F\u002Fdocs.omni.co\u002Fmodeling\u002Ftopics\u002Fsetup.md",[2384],[4143],{"type":51,"value":4144},"Topics setup",{"type":51,"value":4146}," for complete YAML examples with joins, fields, and ai_context, and ",{"type":46,"tag":2380,"props":4148,"children":4151},{"href":4149,"rel":4150},"https:\u002F\u002Fdocs.omni.co\u002Fmodeling\u002Ftopics\u002Fparameters.md",[2384],[4152],{"type":51,"value":4153},"Topic parameters",{"type":51,"value":4155}," for all available options.",{"type":46,"tag":54,"props":4157,"children":4158},{},[4159],{"type":51,"value":4160},"Key topic elements:",{"type":46,"tag":672,"props":4162,"children":4163},{},[4164,4175,4200,4211,4222,4233,4244,4255,4266],{"type":46,"tag":444,"props":4165,"children":4166},{},[4167,4173],{"type":46,"tag":75,"props":4168,"children":4170},{"className":4169},[],[4171],{"type":51,"value":4172},"base_view",{"type":51,"value":4174}," — the primary view for this topic",{"type":46,"tag":444,"props":4176,"children":4177},{},[4178,4184,4186,4192,4193,4199],{"type":46,"tag":75,"props":4179,"children":4181},{"className":4180},[],[4182],{"type":51,"value":4183},"joins",{"type":51,"value":4185}," — nested structure for join chains (e.g., ",{"type":46,"tag":75,"props":4187,"children":4189},{"className":4188},[],[4190],{"type":51,"value":4191},"users: {}",{"type":51,"value":313},{"type":46,"tag":75,"props":4194,"children":4196},{"className":4195},[],[4197],{"type":51,"value":4198},"inventory_items: { products: {} }",{"type":51,"value":3588},{"type":46,"tag":444,"props":4201,"children":4202},{},[4203,4209],{"type":46,"tag":75,"props":4204,"children":4206},{"className":4205},[],[4207],{"type":51,"value":4208},"ai_context",{"type":51,"value":4210}," — guides Blobby's field mapping (e.g., \"Map 'revenue' → total_revenue\")",{"type":46,"tag":444,"props":4212,"children":4213},{},[4214,4220],{"type":46,"tag":75,"props":4215,"children":4217},{"className":4216},[],[4218],{"type":51,"value":4219},"default_filters",{"type":51,"value":4221}," — applied to all queries unless removed",{"type":46,"tag":444,"props":4223,"children":4224},{},[4225,4231],{"type":46,"tag":75,"props":4226,"children":4228},{"className":4227},[],[4229],{"type":51,"value":4230},"always_where_sql",{"type":51,"value":4232}," — non-removable WHERE filter using a SQL expression (cannot be removed by users)",{"type":46,"tag":444,"props":4234,"children":4235},{},[4236,4242],{"type":46,"tag":75,"props":4237,"children":4239},{"className":4238},[],[4240],{"type":51,"value":4241},"always_where_filters",{"type":51,"value":4243}," — non-removable WHERE filter using filter specifications (cannot be removed by users)",{"type":46,"tag":444,"props":4245,"children":4246},{},[4247,4253],{"type":46,"tag":75,"props":4248,"children":4250},{"className":4249},[],[4251],{"type":51,"value":4252},"always_having_sql",{"type":51,"value":4254}," — non-removable HAVING filter using a SQL expression, applied after aggregation (cannot be removed by users)",{"type":46,"tag":444,"props":4256,"children":4257},{},[4258,4264],{"type":46,"tag":75,"props":4259,"children":4261},{"className":4260},[],[4262],{"type":51,"value":4263},"always_having_filters",{"type":51,"value":4265}," — non-removable HAVING filter using filter specifications, applied after aggregation (cannot be removed by users)",{"type":46,"tag":444,"props":4267,"children":4268},{},[4269,4275,4277],{"type":46,"tag":75,"props":4270,"children":4272},{"className":4271},[],[4273],{"type":51,"value":4274},"fields",{"type":51,"value":4276}," — field curation: ",{"type":46,"tag":75,"props":4278,"children":4280},{"className":4279},[],[4281],{"type":51,"value":4282},"[order_items.*, users.name, -users.internal_id]",{"type":46,"tag":799,"props":4284,"children":4286},{"id":4285},"filter-expressions-for-topics",[4287],{"type":51,"value":4288},"Filter Expressions for Topics",{"type":46,"tag":54,"props":4290,"children":4291},{},[4292,4294,4299,4300,4305,4307,4312,4314,4319],{"type":51,"value":4293},"When configuring ",{"type":46,"tag":75,"props":4295,"children":4297},{"className":4296},[],[4298],{"type":51,"value":4219},{"type":51,"value":1785},{"type":46,"tag":75,"props":4301,"children":4303},{"className":4302},[],[4304],{"type":51,"value":4241},{"type":51,"value":4306},", or ",{"type":46,"tag":75,"props":4308,"children":4310},{"className":4309},[],[4311],{"type":51,"value":4263},{"type":51,"value":4313}," on a topic, use the YAML filter condition syntax — the same syntax used in measure filters. See ",{"type":46,"tag":75,"props":4315,"children":4317},{"className":4316},[],[4318],{"type":51,"value":3616},{"type":51,"value":4320}," for the complete reference.",{"type":46,"tag":54,"props":4322,"children":4323},{},[4324],{"type":51,"value":4325},"If the right filter configuration for a given use case isn't obvious, use the Omni AI CLI to search the docs:",{"type":46,"tag":91,"props":4327,"children":4329},{"className":93,"code":4328,"language":95,"meta":96,"style":96},"omni ai search-omni-docs \"how do I configure always_where_filters on a topic in Omni?\"\n",[4330],{"type":46,"tag":75,"props":4331,"children":4332},{"__ignoreMap":96},[4333],{"type":46,"tag":102,"props":4334,"children":4335},{"class":104,"line":105},[4336,4340,4345,4350,4354,4359],{"type":46,"tag":102,"props":4337,"children":4338},{"style":198},[4339],{"type":51,"value":15},{"type":46,"tag":102,"props":4341,"children":4342},{"style":133},[4343],{"type":51,"value":4344}," ai",{"type":46,"tag":102,"props":4346,"children":4347},{"style":133},[4348],{"type":51,"value":4349}," search-omni-docs",{"type":46,"tag":102,"props":4351,"children":4352},{"style":144},[4353],{"type":51,"value":167},{"type":46,"tag":102,"props":4355,"children":4356},{"style":133},[4357],{"type":51,"value":4358},"how do I configure always_where_filters on a topic in Omni?",{"type":46,"tag":102,"props":4360,"children":4361},{"style":144},[4362],{"type":51,"value":177},{"type":46,"tag":54,"props":4364,"children":4365},{},[4366],{"type":51,"value":4367},"Use targeted questions to get precise YAML examples for your specific filtering need before writing the model YAML.",{"type":46,"tag":84,"props":4369,"children":4371},{"id":4370},"writing-relationships",[4372],{"type":51,"value":4373},"Writing Relationships",{"type":46,"tag":799,"props":4375,"children":4377},{"id":4376},"global-relationships",[4378],{"type":51,"value":4379},"Global Relationships",{"type":46,"tag":54,"props":4381,"children":4382},{},[4383],{"type":51,"value":4384},"Global relationships are defined in the shared relationships file and are available across all topics. Use these for standard, reusable joins.",{"type":46,"tag":91,"props":4386,"children":4388},{"className":2470,"code":4387,"language":19,"meta":96,"style":96},"- join_from_view: order_items\n  join_to_view: users\n  on_sql: ${order_items.user_id} = ${users.id}\n  relationship_type: many_to_one\n  join_type: always_left\n",[4389],{"type":46,"tag":75,"props":4390,"children":4391},{"__ignoreMap":96},[4392,4414,4431,4448,4465],{"type":46,"tag":102,"props":4393,"children":4394},{"class":104,"line":105},[4395,4400,4405,4409],{"type":46,"tag":102,"props":4396,"children":4397},{"style":144},[4398],{"type":51,"value":4399},"-",{"type":46,"tag":102,"props":4401,"children":4402},{"style":2481},[4403],{"type":51,"value":4404}," join_from_view",{"type":46,"tag":102,"props":4406,"children":4407},{"style":144},[4408],{"type":51,"value":2514},{"type":46,"tag":102,"props":4410,"children":4411},{"style":133},[4412],{"type":51,"value":4413}," order_items\n",{"type":46,"tag":102,"props":4415,"children":4416},{"class":104,"line":115},[4417,4422,4426],{"type":46,"tag":102,"props":4418,"children":4419},{"style":2481},[4420],{"type":51,"value":4421},"  join_to_view",{"type":46,"tag":102,"props":4423,"children":4424},{"style":144},[4425],{"type":51,"value":2514},{"type":46,"tag":102,"props":4427,"children":4428},{"style":133},[4429],{"type":51,"value":4430}," users\n",{"type":46,"tag":102,"props":4432,"children":4433},{"class":104,"line":33},[4434,4439,4443],{"type":46,"tag":102,"props":4435,"children":4436},{"style":2481},[4437],{"type":51,"value":4438},"  on_sql",{"type":46,"tag":102,"props":4440,"children":4441},{"style":144},[4442],{"type":51,"value":2514},{"type":46,"tag":102,"props":4444,"children":4445},{"style":133},[4446],{"type":51,"value":4447}," ${order_items.user_id} = ${users.id}\n",{"type":46,"tag":102,"props":4449,"children":4450},{"class":104,"line":221},[4451,4456,4460],{"type":46,"tag":102,"props":4452,"children":4453},{"style":2481},[4454],{"type":51,"value":4455},"  relationship_type",{"type":46,"tag":102,"props":4457,"children":4458},{"style":144},[4459],{"type":51,"value":2514},{"type":46,"tag":102,"props":4461,"children":4462},{"style":133},[4463],{"type":51,"value":4464}," many_to_one\n",{"type":46,"tag":102,"props":4466,"children":4467},{"class":104,"line":259},[4468,4473,4477],{"type":46,"tag":102,"props":4469,"children":4470},{"style":2481},[4471],{"type":51,"value":4472},"  join_type",{"type":46,"tag":102,"props":4474,"children":4475},{"style":144},[4476],{"type":51,"value":2514},{"type":46,"tag":102,"props":4478,"children":4479},{"style":133},[4480],{"type":51,"value":4481}," always_left\n",{"type":46,"tag":2161,"props":4483,"children":4484},{},[4485,4500],{"type":46,"tag":2165,"props":4486,"children":4487},{},[4488],{"type":46,"tag":2169,"props":4489,"children":4490},{},[4491,4495],{"type":46,"tag":2173,"props":4492,"children":4493},{},[4494],{"type":51,"value":2177},{"type":46,"tag":2173,"props":4496,"children":4497},{},[4498],{"type":51,"value":4499},"When to Use",{"type":46,"tag":2189,"props":4501,"children":4502},{},[4503,4520,4537,4554],{"type":46,"tag":2169,"props":4504,"children":4505},{},[4506,4515],{"type":46,"tag":2196,"props":4507,"children":4508},{},[4509],{"type":46,"tag":75,"props":4510,"children":4512},{"className":4511},[],[4513],{"type":51,"value":4514},"many_to_one",{"type":46,"tag":2196,"props":4516,"children":4517},{},[4518],{"type":51,"value":4519},"Orders → Users",{"type":46,"tag":2169,"props":4521,"children":4522},{},[4523,4532],{"type":46,"tag":2196,"props":4524,"children":4525},{},[4526],{"type":46,"tag":75,"props":4527,"children":4529},{"className":4528},[],[4530],{"type":51,"value":4531},"one_to_many",{"type":46,"tag":2196,"props":4533,"children":4534},{},[4535],{"type":51,"value":4536},"Users → Orders",{"type":46,"tag":2169,"props":4538,"children":4539},{},[4540,4549],{"type":46,"tag":2196,"props":4541,"children":4542},{},[4543],{"type":46,"tag":75,"props":4544,"children":4546},{"className":4545},[],[4547],{"type":51,"value":4548},"one_to_one",{"type":46,"tag":2196,"props":4550,"children":4551},{},[4552],{"type":51,"value":4553},"Users → User Settings",{"type":46,"tag":2169,"props":4555,"children":4556},{},[4557,4566],{"type":46,"tag":2196,"props":4558,"children":4559},{},[4560],{"type":46,"tag":75,"props":4561,"children":4563},{"className":4562},[],[4564],{"type":51,"value":4565},"many_to_many",{"type":46,"tag":2196,"props":4567,"children":4568},{},[4569],{"type":51,"value":4570},"Tags ↔ Products (rare)",{"type":46,"tag":54,"props":4572,"children":4573},{},[4574,4576,4582],{"type":51,"value":4575},"Getting ",{"type":46,"tag":75,"props":4577,"children":4579},{"className":4578},[],[4580],{"type":51,"value":4581},"relationship_type",{"type":51,"value":4583}," right prevents fanout and symmetric aggregate errors.",{"type":46,"tag":799,"props":4585,"children":4587},{"id":4586},"topic-scoped-relationships",[4588],{"type":51,"value":4589},"Topic-Scoped Relationships",{"type":46,"tag":60,"props":4591,"children":4592},{},[4593],{"type":46,"tag":54,"props":4594,"children":4595},{},[4596,4601,4603,4609,4611,4617,4619,4624],{"type":46,"tag":67,"props":4597,"children":4598},{},[4599],{"type":51,"value":4600},"Before defining, check the global relationships file",{"type":51,"value":4602}," for a join between the same two views in either direction. Same ",{"type":46,"tag":75,"props":4604,"children":4606},{"className":4605},[],[4607],{"type":51,"value":4608},"on_sql",{"type":51,"value":4610}," → redundant, use ",{"type":46,"tag":75,"props":4612,"children":4614},{"className":4613},[],[4615],{"type":51,"value":4616},"joins:",{"type":51,"value":4618}," only. Different ",{"type":46,"tag":75,"props":4620,"children":4622},{"className":4621},[],[4623],{"type":51,"value":4608},{"type":51,"value":4625}," → default to the extended views pattern below rather than a silent override. Confirm intent with the modeler.",{"type":46,"tag":54,"props":4627,"children":4628},{},[4629],{"type":51,"value":4630},"Use topic-scoped relationships for one-off joins not in the shared model, or joining the same table multiple times under different conditions.",{"type":46,"tag":91,"props":4632,"children":4634},{"className":2470,"code":4633,"language":19,"meta":96,"style":96},"# .topic file\nrelationships:\n  - join_from_view: order_items\n    join_to_view: users\n    on_sql: ${order_items.user_id} = ${users.id}\n    relationship_type: many_to_one\n    join_type: always_left\n\njoins:\n  users: {}\n",[4635],{"type":46,"tag":75,"props":4636,"children":4637},{"__ignoreMap":96},[4638,4646,4657,4677,4693,4709,4725,4741,4748,4759],{"type":46,"tag":102,"props":4639,"children":4640},{"class":104,"line":105},[4641],{"type":46,"tag":102,"props":4642,"children":4643},{"style":109},[4644],{"type":51,"value":4645},"# .topic file\n",{"type":46,"tag":102,"props":4647,"children":4648},{"class":104,"line":115},[4649,4653],{"type":46,"tag":102,"props":4650,"children":4651},{"style":2481},[4652],{"type":51,"value":2459},{"type":46,"tag":102,"props":4654,"children":4655},{"style":144},[4656],{"type":51,"value":2489},{"type":46,"tag":102,"props":4658,"children":4659},{"class":104,"line":33},[4660,4665,4669,4673],{"type":46,"tag":102,"props":4661,"children":4662},{"style":144},[4663],{"type":51,"value":4664},"  -",{"type":46,"tag":102,"props":4666,"children":4667},{"style":2481},[4668],{"type":51,"value":4404},{"type":46,"tag":102,"props":4670,"children":4671},{"style":144},[4672],{"type":51,"value":2514},{"type":46,"tag":102,"props":4674,"children":4675},{"style":133},[4676],{"type":51,"value":4413},{"type":46,"tag":102,"props":4678,"children":4679},{"class":104,"line":221},[4680,4685,4689],{"type":46,"tag":102,"props":4681,"children":4682},{"style":2481},[4683],{"type":51,"value":4684},"    join_to_view",{"type":46,"tag":102,"props":4686,"children":4687},{"style":144},[4688],{"type":51,"value":2514},{"type":46,"tag":102,"props":4690,"children":4691},{"style":133},[4692],{"type":51,"value":4430},{"type":46,"tag":102,"props":4694,"children":4695},{"class":104,"line":259},[4696,4701,4705],{"type":46,"tag":102,"props":4697,"children":4698},{"style":2481},[4699],{"type":51,"value":4700},"    on_sql",{"type":46,"tag":102,"props":4702,"children":4703},{"style":144},[4704],{"type":51,"value":2514},{"type":46,"tag":102,"props":4706,"children":4707},{"style":133},[4708],{"type":51,"value":4447},{"type":46,"tag":102,"props":4710,"children":4711},{"class":104,"line":269},[4712,4717,4721],{"type":46,"tag":102,"props":4713,"children":4714},{"style":2481},[4715],{"type":51,"value":4716},"    relationship_type",{"type":46,"tag":102,"props":4718,"children":4719},{"style":144},[4720],{"type":51,"value":2514},{"type":46,"tag":102,"props":4722,"children":4723},{"style":133},[4724],{"type":51,"value":4464},{"type":46,"tag":102,"props":4726,"children":4727},{"class":104,"line":278},[4728,4733,4737],{"type":46,"tag":102,"props":4729,"children":4730},{"style":2481},[4731],{"type":51,"value":4732},"    join_type",{"type":46,"tag":102,"props":4734,"children":4735},{"style":144},[4736],{"type":51,"value":2514},{"type":46,"tag":102,"props":4738,"children":4739},{"style":133},[4740],{"type":51,"value":4481},{"type":46,"tag":102,"props":4742,"children":4743},{"class":104,"line":1492},[4744],{"type":46,"tag":102,"props":4745,"children":4746},{"emptyLinePlaceholder":263},[4747],{"type":51,"value":266},{"type":46,"tag":102,"props":4749,"children":4750},{"class":104,"line":2072},[4751,4755],{"type":46,"tag":102,"props":4752,"children":4753},{"style":2481},[4754],{"type":51,"value":4183},{"type":46,"tag":102,"props":4756,"children":4757},{"style":144},[4758],{"type":51,"value":2489},{"type":46,"tag":102,"props":4760,"children":4761},{"class":104,"line":2604},[4762,4767,4771],{"type":46,"tag":102,"props":4763,"children":4764},{"style":2481},[4765],{"type":51,"value":4766},"  users",{"type":46,"tag":102,"props":4768,"children":4769},{"style":144},[4770],{"type":51,"value":2514},{"type":46,"tag":102,"props":4772,"children":4773},{"style":144},[4774],{"type":51,"value":4775}," {}\n",{"type":46,"tag":60,"props":4777,"children":4778},{},[4779],{"type":46,"tag":54,"props":4780,"children":4781},{},[4782,4798,4799,4804,4806,4811,4813,4818],{"type":46,"tag":67,"props":4783,"children":4784},{},[4785,4790,4792,4797],{"type":46,"tag":75,"props":4786,"children":4788},{"className":4787},[],[4789],{"type":51,"value":4183},{"type":51,"value":4791}," vs ",{"type":46,"tag":75,"props":4793,"children":4795},{"className":4794},[],[4796],{"type":51,"value":2459},{"type":51,"value":2514},{"type":51,"value":1254},{"type":46,"tag":75,"props":4800,"children":4802},{"className":4801},[],[4803],{"type":51,"value":4183},{"type":51,"value":4805}," declares which views are in the topic and their hierarchy; ",{"type":46,"tag":75,"props":4807,"children":4809},{"className":4808},[],[4810],{"type":51,"value":2459},{"type":51,"value":4812}," defines the join conditions. A topic using only global relationships needs only ",{"type":46,"tag":75,"props":4814,"children":4816},{"className":4815},[],[4817],{"type":51,"value":4183},{"type":51,"value":4819},". A topic with a one-off join needs both.",{"type":46,"tag":1661,"props":4821,"children":4823},{"id":4822},"extended-views-joining-the-same-table-multiple-ways",[4824],{"type":51,"value":4825},"Extended Views: Joining the Same Table Multiple Ways",{"type":46,"tag":54,"props":4827,"children":4828},{},[4829,4831,4837,4839,4844,4846,4852],{"type":51,"value":4830},"When the same table needs multiple joins (e.g., ",{"type":46,"tag":75,"props":4832,"children":4834},{"className":4833},[],[4835],{"type":51,"value":4836},"users",{"type":51,"value":4838}," as buyer and seller), use the ",{"type":46,"tag":67,"props":4840,"children":4841},{},[4842],{"type":51,"value":4843},"extended views",{"type":51,"value":4845}," pattern — not ",{"type":46,"tag":75,"props":4847,"children":4849},{"className":4848},[],[4850],{"type":51,"value":4851},"join_to_view_as",{"type":51,"value":4853},". Two variants:",{"type":46,"tag":54,"props":4855,"children":4856},{},[4857,4862,4864,4869,4871,4877,4879,4885],{"type":46,"tag":67,"props":4858,"children":4859},{},[4860],{"type":51,"value":4861},"Variant 1 — Global (reusable):",{"type":51,"value":4863}," Create a standalone ",{"type":46,"tag":75,"props":4865,"children":4867},{"className":4866},[],[4868],{"type":51,"value":2209},{"type":51,"value":4870}," file with ",{"type":46,"tag":75,"props":4872,"children":4874},{"className":4873},[],[4875],{"type":51,"value":4876},"extends:",{"type":51,"value":4878},", a role-descriptive name, and a ",{"type":46,"tag":75,"props":4880,"children":4882},{"className":4881},[],[4883],{"type":51,"value":4884},"description:",{"type":51,"value":4886},". Define the relationship globally — any topic can then join it like any other view.",{"type":46,"tag":54,"props":4888,"children":4889},{},[4890,4895,4897,4902],{"type":46,"tag":67,"props":4891,"children":4892},{},[4893],{"type":51,"value":4894},"Variant 2 — Topic-scoped (inline):",{"type":51,"value":4896}," Define the alias in the topic's ",{"type":46,"tag":75,"props":4898,"children":4900},{"className":4899},[],[4901],{"type":51,"value":3802},{"type":51,"value":4903}," block with its relationship in the same file. Use when the alias is not generally applicable in other topics.",{"type":46,"tag":54,"props":4905,"children":4906},{},[4907,4908,4914],{"type":51,"value":656},{"type":46,"tag":75,"props":4909,"children":4911},{"className":4910},[],[4912],{"type":51,"value":4913},"references\u002Ftopic-scoped-relationships.md",{"type":51,"value":4915}," for full YAML examples of both variants.",{"type":46,"tag":60,"props":4917,"children":4918},{},[4919],{"type":46,"tag":54,"props":4920,"children":4921},{},[4922,4924,4930],{"type":51,"value":4923},"If you see a ",{"type":46,"tag":75,"props":4925,"children":4927},{"className":4926},[],[4928],{"type":51,"value":4929},"relationship alias duplicates view name",{"type":51,"value":4931}," error, this pattern is the fix.",{"type":46,"tag":799,"props":4933,"children":4935},{"id":4934},"topic-scoped-view-definitions",[4936],{"type":51,"value":4937},"Topic-Scoped View Definitions",{"type":46,"tag":54,"props":4939,"children":4940},{},[4941,4943,4948,4950,4956,4958,4963],{"type":51,"value":4942},"Topics can define or override views inline using a ",{"type":46,"tag":75,"props":4944,"children":4946},{"className":4945},[],[4947],{"type":51,"value":3802},{"type":51,"value":4949}," block — controlling ",{"type":46,"tag":75,"props":4951,"children":4953},{"className":4952},[],[4954],{"type":51,"value":4955},"display_order",{"type":51,"value":4957},", overriding ",{"type":46,"tag":75,"props":4959,"children":4961},{"className":4960},[],[4962],{"type":51,"value":3490},{"type":51,"value":4964},", adding topic-specific filtered measures or derived dimensions, defining cross-view fields, and joining the same view multiple ways with per-alias conditions.",{"type":46,"tag":60,"props":4966,"children":4967},{},[4968,4976],{"type":46,"tag":54,"props":4969,"children":4970},{},[4971],{"type":46,"tag":67,"props":4972,"children":4973},{},[4974],{"type":51,"value":4975},"Before adding any topic-scoped field to an existing view:",{"type":46,"tag":440,"props":4977,"children":4978},{},[4979,4992],{"type":46,"tag":444,"props":4980,"children":4981},{},[4982,4984,4990],{"type":51,"value":4983},"Read the view YAML (",{"type":46,"tag":75,"props":4985,"children":4987},{"className":4986},[],[4988],{"type":51,"value":4989},"omni models yaml-get",{"type":51,"value":4991},") and confirm the field doesn't already exist. If it does with the same definition, skip it.",{"type":46,"tag":444,"props":4993,"children":4994},{},[4995],{"type":51,"value":4996},"If a field with the same name exists but uses different SQL, this is an override. Confirm explicitly with the modeler — queries through this topic will use the topic-scoped definition; all other topics keep the shared one.",{"type":46,"tag":91,"props":4998,"children":5000},{"className":2470,"code":4999,"language":19,"meta":96,"style":96},"# Example: display order + topic-specific filtered measure\nviews:\n  order_items:\n    display_order: 0\n    measures:\n      us_revenue:\n        sql: ${sale_price}\n        aggregate_type: sum\n        format: currency_2\n        filters:\n          users.country:\n            is: US\n",[5001],{"type":46,"tag":75,"props":5002,"children":5003},{"__ignoreMap":96},[5004,5012,5024,5036,5054,5066,5078,5094,5110,5126,5138,5150],{"type":46,"tag":102,"props":5005,"children":5006},{"class":104,"line":105},[5007],{"type":46,"tag":102,"props":5008,"children":5009},{"style":109},[5010],{"type":51,"value":5011},"# Example: display order + topic-specific filtered measure\n",{"type":46,"tag":102,"props":5013,"children":5014},{"class":104,"line":115},[5015,5020],{"type":46,"tag":102,"props":5016,"children":5017},{"style":2481},[5018],{"type":51,"value":5019},"views",{"type":46,"tag":102,"props":5021,"children":5022},{"style":144},[5023],{"type":51,"value":2489},{"type":46,"tag":102,"props":5025,"children":5026},{"class":104,"line":33},[5027,5032],{"type":46,"tag":102,"props":5028,"children":5029},{"style":2481},[5030],{"type":51,"value":5031},"  order_items",{"type":46,"tag":102,"props":5033,"children":5034},{"style":144},[5035],{"type":51,"value":2489},{"type":46,"tag":102,"props":5037,"children":5038},{"class":104,"line":221},[5039,5044,5048],{"type":46,"tag":102,"props":5040,"children":5041},{"style":2481},[5042],{"type":51,"value":5043},"    display_order",{"type":46,"tag":102,"props":5045,"children":5046},{"style":144},[5047],{"type":51,"value":2514},{"type":46,"tag":102,"props":5049,"children":5051},{"style":5050},"--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C",[5052],{"type":51,"value":5053}," 0\n",{"type":46,"tag":102,"props":5055,"children":5056},{"class":104,"line":259},[5057,5062],{"type":46,"tag":102,"props":5058,"children":5059},{"style":2481},[5060],{"type":51,"value":5061},"    measures",{"type":46,"tag":102,"props":5063,"children":5064},{"style":144},[5065],{"type":51,"value":2489},{"type":46,"tag":102,"props":5067,"children":5068},{"class":104,"line":269},[5069,5074],{"type":46,"tag":102,"props":5070,"children":5071},{"style":2481},[5072],{"type":51,"value":5073},"      us_revenue",{"type":46,"tag":102,"props":5075,"children":5076},{"style":144},[5077],{"type":51,"value":2489},{"type":46,"tag":102,"props":5079,"children":5080},{"class":104,"line":278},[5081,5086,5090],{"type":46,"tag":102,"props":5082,"children":5083},{"style":2481},[5084],{"type":51,"value":5085},"        sql",{"type":46,"tag":102,"props":5087,"children":5088},{"style":144},[5089],{"type":51,"value":2514},{"type":46,"tag":102,"props":5091,"children":5092},{"style":133},[5093],{"type":51,"value":2650},{"type":46,"tag":102,"props":5095,"children":5096},{"class":104,"line":1492},[5097,5102,5106],{"type":46,"tag":102,"props":5098,"children":5099},{"style":2481},[5100],{"type":51,"value":5101},"        aggregate_type",{"type":46,"tag":102,"props":5103,"children":5104},{"style":144},[5105],{"type":51,"value":2514},{"type":46,"tag":102,"props":5107,"children":5108},{"style":133},[5109],{"type":51,"value":2667},{"type":46,"tag":102,"props":5111,"children":5112},{"class":104,"line":2072},[5113,5118,5122],{"type":46,"tag":102,"props":5114,"children":5115},{"style":2481},[5116],{"type":51,"value":5117},"        format",{"type":46,"tag":102,"props":5119,"children":5120},{"style":144},[5121],{"type":51,"value":2514},{"type":46,"tag":102,"props":5123,"children":5124},{"style":133},[5125],{"type":51,"value":2685},{"type":46,"tag":102,"props":5127,"children":5128},{"class":104,"line":2604},[5129,5134],{"type":46,"tag":102,"props":5130,"children":5131},{"style":2481},[5132],{"type":51,"value":5133},"        filters",{"type":46,"tag":102,"props":5135,"children":5136},{"style":144},[5137],{"type":51,"value":2489},{"type":46,"tag":102,"props":5139,"children":5140},{"class":104,"line":2622},[5141,5146],{"type":46,"tag":102,"props":5142,"children":5143},{"style":2481},[5144],{"type":51,"value":5145},"          users.country",{"type":46,"tag":102,"props":5147,"children":5148},{"style":144},[5149],{"type":51,"value":2489},{"type":46,"tag":102,"props":5151,"children":5152},{"class":104,"line":2635},[5153,5158,5162],{"type":46,"tag":102,"props":5154,"children":5155},{"style":2481},[5156],{"type":51,"value":5157},"            is",{"type":46,"tag":102,"props":5159,"children":5160},{"style":144},[5161],{"type":51,"value":2514},{"type":46,"tag":102,"props":5163,"children":5164},{"style":133},[5165],{"type":51,"value":5166}," US\n",{"type":46,"tag":54,"props":5168,"children":5169},{},[5170,5171,5177],{"type":51,"value":656},{"type":46,"tag":75,"props":5172,"children":5174},{"className":5173},[],[5175],{"type":51,"value":5176},"references\u002Ftopic-scoped-views.md",{"type":51,"value":5178}," for a full pattern gallery (label overrides, derived dimensions, cross-view fields, multi-join lifecycle, topic-scoped query views).",{"type":46,"tag":60,"props":5180,"children":5181},{},[5182],{"type":46,"tag":54,"props":5183,"children":5184},{},[5185,5197,5199,5205,5207,5212],{"type":46,"tag":67,"props":5186,"children":5187},{},[5188,5190,5195],{"type":51,"value":5189},"Cross-view fields in ",{"type":46,"tag":75,"props":5191,"children":5193},{"className":5192},[],[5194],{"type":51,"value":3802},{"type":51,"value":5196}," blocks:",{"type":51,"value":5198}," Before writing ",{"type":46,"tag":75,"props":5200,"children":5202},{"className":5201},[],[5203],{"type":51,"value":5204},"${view_name.field_name}",{"type":51,"value":5206}," references, confirm every referenced view is declared in the topic's ",{"type":46,"tag":75,"props":5208,"children":5210},{"className":5209},[],[5211],{"type":51,"value":4616},{"type":51,"value":5213}," block — the model validator throws errors for any reference to a view that isn't joined.",{"type":46,"tag":54,"props":5215,"children":5216},{},[5217,5222,5224,5229,5231,5236,5238,5243,5245,5251,5253,5258],{"type":46,"tag":67,"props":5218,"children":5219},{},[5220],{"type":51,"value":5221},"Joining the same view multiple ways",{"type":51,"value":5223}," (e.g., ARR at Start \u002F Current \u002F End): Use ",{"type":46,"tag":75,"props":5225,"children":5227},{"className":5226},[],[5228],{"type":51,"value":4876},{"type":51,"value":5230}," inside the topic's ",{"type":46,"tag":75,"props":5232,"children":5234},{"className":5233},[],[5235],{"type":51,"value":3802},{"type":51,"value":5237}," block to create named aliases, each with its own ",{"type":46,"tag":75,"props":5239,"children":5241},{"className":5240},[],[5242],{"type":51,"value":4608},{"type":51,"value":5244}," in ",{"type":46,"tag":75,"props":5246,"children":5248},{"className":5247},[],[5249],{"type":51,"value":5250},"relationships:",{"type":51,"value":5252},". Each alias inherits all base view fields and can override labels independently. For a full YAML example, see ",{"type":46,"tag":75,"props":5254,"children":5256},{"className":5255},[],[5257],{"type":51,"value":5176},{"type":51,"value":386},{"type":46,"tag":54,"props":5260,"children":5261},{},[5262,5267,5269,5274,5276,5281,5282,5288,5290,5295,5297,5302,5304,5309],{"type":46,"tag":67,"props":5263,"children":5264},{},[5265],{"type":51,"value":5266},"Topic-scoped query views:",{"type":51,"value":5268}," A query view can also be defined inside a topic's ",{"type":46,"tag":75,"props":5270,"children":5272},{"className":5271},[],[5273],{"type":51,"value":3802},{"type":51,"value":5275}," block, scoping it to that topic only. Same primary key rules apply (",{"type":46,"tag":75,"props":5277,"children":5279},{"className":5278},[],[5280],{"type":51,"value":2296},{"type":51,"value":313},{"type":46,"tag":75,"props":5283,"children":5285},{"className":5284},[],[5286],{"type":51,"value":5287},"custom_compound_primary_key_sql",{"type":51,"value":5289},"). Include a ",{"type":46,"tag":75,"props":5291,"children":5293},{"className":5292},[],[5294],{"type":51,"value":5250},{"type":51,"value":5296}," entry and a ",{"type":46,"tag":75,"props":5298,"children":5300},{"className":5299},[],[5301],{"type":51,"value":4616},{"type":51,"value":5303}," entry for the new view — see Query Views section above, and ",{"type":46,"tag":75,"props":5305,"children":5307},{"className":5306},[],[5308],{"type":51,"value":5176},{"type":51,"value":5310}," for a complete example.",{"type":46,"tag":84,"props":5312,"children":5314},{"id":5313},"query-views",[5315],{"type":51,"value":5316},"Query Views",{"type":46,"tag":54,"props":5318,"children":5319},{},[5320,5322,5327,5329,5334,5336,5341,5343,5348],{"type":51,"value":5321},"Virtual tables defined by a saved query. A query view must have a primary key or it cannot be joined without producing fanout errors. ",{"type":46,"tag":67,"props":5323,"children":5324},{},[5325],{"type":51,"value":5326},"Before writing, confirm which field uniquely identifies each row — unless the primary key can be clearly inferred from the query itself and the involved views",{"type":51,"value":5328}," (e.g. a query that selects ",{"type":46,"tag":75,"props":5330,"children":5332},{"className":5331},[],[5333],{"type":51,"value":2320},{"type":51,"value":5335}," from a ",{"type":46,"tag":75,"props":5337,"children":5339},{"className":5338},[],[5340],{"type":51,"value":4836},{"type":51,"value":5342}," view where ",{"type":46,"tag":75,"props":5344,"children":5346},{"className":5345},[],[5347],{"type":51,"value":2320},{"type":51,"value":5349}," is the known primary key).",{"type":46,"tag":54,"props":5351,"children":5352},{},[5353],{"type":51,"value":5354},"There are two ways to define the primary key:",{"type":46,"tag":54,"props":5356,"children":5357},{},[5358,5363,5365,5370,5372,5378],{"type":46,"tag":67,"props":5359,"children":5360},{},[5361],{"type":51,"value":5362},"Option 1 — Single unique field:",{"type":51,"value":5364}," Mark exactly one dimension ",{"type":46,"tag":75,"props":5366,"children":5368},{"className":5367},[],[5369],{"type":51,"value":2296},{"type":51,"value":5371}," in the ",{"type":46,"tag":75,"props":5373,"children":5375},{"className":5374},[],[5376],{"type":51,"value":5377},"dimensions:",{"type":51,"value":5379}," block.",{"type":46,"tag":54,"props":5381,"children":5382},{},[5383,5388,5390,5396,5398,5403],{"type":46,"tag":67,"props":5384,"children":5385},{},[5386],{"type":51,"value":5387},"Option 2 — Compound key:",{"type":51,"value":5389}," When no single field is unique but a combination is, set ",{"type":46,"tag":75,"props":5391,"children":5393},{"className":5392},[],[5394],{"type":51,"value":5395},"custom_compound_primary_key_sql: [field_a, field_b]",{"type":51,"value":5397}," at the view level — no ",{"type":46,"tag":75,"props":5399,"children":5401},{"className":5400},[],[5402],{"type":51,"value":2296},{"type":51,"value":5404}," dimension needed.",{"type":46,"tag":54,"props":5406,"children":5407},{},[5408,5410,5416,5418,5423,5425,5430,5432,5438,5440,5446,5448,5454],{"type":51,"value":5409},"Both options work with either a ",{"type":46,"tag":75,"props":5411,"children":5413},{"className":5412},[],[5414],{"type":51,"value":5415},"query:",{"type":51,"value":5417}," block (field-mapped virtual table) or a ",{"type":46,"tag":75,"props":5419,"children":5421},{"className":5420},[],[5422],{"type":51,"value":3455},{"type":51,"value":5424}," block (raw SELECT). In ",{"type":46,"tag":75,"props":5426,"children":5428},{"className":5427},[],[5429],{"type":51,"value":3455},{"type":51,"value":5431}," blocks, use ",{"type":46,"tag":75,"props":5433,"children":5435},{"className":5434},[],[5436],{"type":51,"value":5437},"${view_name}",{"type":51,"value":5439}," to reference a view's underlying table rather than a hard-coded ",{"type":46,"tag":75,"props":5441,"children":5443},{"className":5442},[],[5444],{"type":51,"value":5445},"CATALOG.SCHEMA.TABLE",{"type":51,"value":5447}," path — it's preferred and stays correct if the table moves. See ",{"type":46,"tag":75,"props":5449,"children":5451},{"className":5450},[],[5452],{"type":51,"value":5453},"references\u002Fquery-view-examples.md",{"type":51,"value":5455}," for complete YAML for each variant.",{"type":46,"tag":60,"props":5457,"children":5458},{},[5459],{"type":46,"tag":54,"props":5460,"children":5461},{},[5462,5464],{"type":51,"value":5463},"If the user is unsure which field is unique, ask before writing the view. A query view without a primary key will trigger a \"Joins fan out the data without a primary key\" error when joined. See: ",{"type":46,"tag":2380,"props":5465,"children":5468},{"href":5466,"rel":5467},"https:\u002F\u002Fcommunity.omni.co\u002Ft\u002Fwhy-am-i-getting-the-error-joins-fan-out-the-data-without-a-primary-key\u002F37",[2384],[5469],{"type":51,"value":5466},{"type":46,"tag":54,"props":5471,"children":5472},{},[5473,5475,5480,5482,5487],{"type":51,"value":5474},"Query views can also be defined inline within a topic's ",{"type":46,"tag":75,"props":5476,"children":5478},{"className":5477},[],[5479],{"type":51,"value":3802},{"type":51,"value":5481}," block, scoping the virtual table to that topic only. See ",{"type":46,"tag":75,"props":5483,"children":5485},{"className":5484},[],[5486],{"type":51,"value":5176},{"type":51,"value":5488}," for an example.",{"type":46,"tag":84,"props":5490,"children":5492},{"id":5491},"common-validation-errors",[5493],{"type":51,"value":5494},"Common Validation Errors",{"type":46,"tag":2161,"props":5496,"children":5497},{},[5498,5514],{"type":46,"tag":2165,"props":5499,"children":5500},{},[5501],{"type":46,"tag":2169,"props":5502,"children":5503},{},[5504,5509],{"type":46,"tag":2173,"props":5505,"children":5506},{},[5507],{"type":51,"value":5508},"Error",{"type":46,"tag":2173,"props":5510,"children":5511},{},[5512],{"type":51,"value":5513},"Fix",{"type":46,"tag":2189,"props":5515,"children":5516},{},[5517,5530,5543,5563,5576,5596,5617],{"type":46,"tag":2169,"props":5518,"children":5519},{},[5520,5525],{"type":46,"tag":2196,"props":5521,"children":5522},{},[5523],{"type":51,"value":5524},"\"No view X\"",{"type":46,"tag":2196,"props":5526,"children":5527},{},[5528],{"type":51,"value":5529},"Check view name spelling",{"type":46,"tag":2169,"props":5531,"children":5532},{},[5533,5538],{"type":46,"tag":2196,"props":5534,"children":5535},{},[5536],{"type":51,"value":5537},"\"No join path from X to Y\"",{"type":46,"tag":2196,"props":5539,"children":5540},{},[5541],{"type":51,"value":5542},"Add a relationship",{"type":46,"tag":2169,"props":5544,"children":5545},{},[5546,5551],{"type":46,"tag":2196,"props":5547,"children":5548},{},[5549],{"type":51,"value":5550},"\"Duplicate field name\"",{"type":46,"tag":2196,"props":5552,"children":5553},{},[5554,5556,5561],{"type":51,"value":5555},"Remove duplicate or rename (or suppress with ",{"type":46,"tag":75,"props":5557,"children":5559},{"className":5558},[],[5560],{"type":51,"value":3519},{"type":51,"value":5562}," if one is auto-generated)",{"type":46,"tag":2169,"props":5564,"children":5565},{},[5566,5571],{"type":46,"tag":2196,"props":5567,"children":5568},{},[5569],{"type":51,"value":5570},"\"Invalid YAML syntax\"",{"type":46,"tag":2196,"props":5572,"children":5573},{},[5574],{"type":51,"value":5575},"Check indentation (2 spaces, no tabs)",{"type":46,"tag":2169,"props":5577,"children":5578},{},[5579,5584],{"type":46,"tag":2196,"props":5580,"children":5581},{},[5582],{"type":51,"value":5583},"Fanout \u002F incorrect aggregations on joins",{"type":46,"tag":2196,"props":5585,"children":5586},{},[5587,5589,5594],{"type":51,"value":5588},"Add ",{"type":46,"tag":75,"props":5590,"children":5592},{"className":5591},[],[5593],{"type":51,"value":2296},{"type":51,"value":5595}," to the joined view — every view that participates in a join must have a primary key",{"type":46,"tag":2169,"props":5597,"children":5598},{},[5599,5612],{"type":46,"tag":2196,"props":5600,"children":5601},{},[5602,5604,5610],{"type":51,"value":5603},"Column reference error (e.g., \"Column ",{"type":46,"tag":75,"props":5605,"children":5607},{"className":5606},[],[5608],{"type":51,"value":5609},"X",{"type":51,"value":5611}," not found\")",{"type":46,"tag":2196,"props":5613,"children":5614},{},[5615],{"type":51,"value":5616},"Check that the table exists and your Omni connection has access",{"type":46,"tag":2169,"props":5618,"children":5619},{},[5620,5625],{"type":46,"tag":2196,"props":5621,"children":5622},{},[5623],{"type":51,"value":5624},"Duplicate view appeared at the repo root after an edit",{"type":46,"tag":2196,"props":5626,"children":5627},{},[5628,5630,5635,5637,5642,5644,5650,5652,5658],{"type":51,"value":5629},"You wrote with a bare ",{"type":46,"tag":75,"props":5631,"children":5633},{"className":5632},[],[5634],{"type":51,"value":1191},{"type":51,"value":5636}," instead of the file's full path. Delete the stray root file (send empty ",{"type":46,"tag":75,"props":5638,"children":5640},{"className":5639},[],[5641],{"type":51,"value":19},{"type":51,"value":5643}," to it) and re-write using the exact ",{"type":46,"tag":75,"props":5645,"children":5647},{"className":5646},[],[5648],{"type":51,"value":5649},"files",{"type":51,"value":5651}," key, including its folder prefix (e.g. ",{"type":46,"tag":75,"props":5653,"children":5655},{"className":5654},[],[5656],{"type":51,"value":5657},"MARTS\u002F",{"type":51,"value":3588},{"type":46,"tag":84,"props":5660,"children":5662},{"id":5661},"troubleshooting-model-out-of-sync-with-database",[5663],{"type":51,"value":5664},"Troubleshooting: Model Out of Sync with Database",{"type":46,"tag":54,"props":5666,"children":5667},{},[5668,5670,5676,5678,5683],{"type":51,"value":5669},"If the model doesn't reflect the database (missing columns\u002Ftables, wrong types, broken references), trigger a schema refresh (see \"Schema Refresh\" above), then ",{"type":46,"tag":75,"props":5671,"children":5673},{"className":5672},[],[5674],{"type":51,"value":5675},"omni models validate \u003CmodelId>",{"type":51,"value":5677},". Field-name collisions and broken column references are usually fixed with ",{"type":46,"tag":75,"props":5679,"children":5681},{"className":5680},[],[5682],{"type":51,"value":3519},{"type":51,"value":5684}," or a rename (see \"Common Validation Errors\"); persistent missing tables mean the connection lacks access to that database\u002Fschema.",{"type":46,"tag":84,"props":5686,"children":5688},{"id":5687},"docs-reference",[5689],{"type":51,"value":5690},"Docs Reference",{"type":46,"tag":672,"props":5692,"children":5693},{},[5694],{"type":46,"tag":444,"props":5695,"children":5696},{},[5697,5704,5706,5713,5714,5720,5721,5728,5729,5736,5737,5743,5744,5750,5751],{"type":46,"tag":2380,"props":5698,"children":5701},{"href":5699,"rel":5700},"https:\u002F\u002Fdocs.omni.co\u002Fapi\u002Fmodels.md",[2384],[5702],{"type":51,"value":5703},"Model YAML API",{"type":51,"value":5705}," · ",{"type":46,"tag":2380,"props":5707,"children":5710},{"href":5708,"rel":5709},"https:\u002F\u002Fdocs.omni.co\u002Fmodeling\u002Fviews.md",[2384],[5711],{"type":51,"value":5712},"Views",{"type":51,"value":5705},{"type":46,"tag":2380,"props":5715,"children":5717},{"href":4149,"rel":5716},[2384],[5718],{"type":51,"value":5719},"Topics",{"type":51,"value":5705},{"type":46,"tag":2380,"props":5722,"children":5725},{"href":5723,"rel":5724},"https:\u002F\u002Fdocs.omni.co\u002Fmodeling\u002Fdimensions.md",[2384],[5726],{"type":51,"value":5727},"Dimensions",{"type":51,"value":5705},{"type":46,"tag":2380,"props":5730,"children":5733},{"href":5731,"rel":5732},"https:\u002F\u002Fdocs.omni.co\u002Fmodeling\u002Fmeasures.md",[2384],[5734],{"type":51,"value":5735},"Measures",{"type":51,"value":5705},{"type":46,"tag":2380,"props":5738,"children":5741},{"href":5739,"rel":5740},"https:\u002F\u002Fdocs.omni.co\u002Fmodeling\u002Frelationships.md",[2384],[5742],{"type":51,"value":2244},{"type":51,"value":5705},{"type":46,"tag":2380,"props":5745,"children":5748},{"href":5746,"rel":5747},"https:\u002F\u002Fdocs.omni.co\u002Fmodeling\u002Fquery-views.md",[2384],[5749],{"type":51,"value":5316},{"type":51,"value":5705},{"type":46,"tag":2380,"props":5752,"children":5755},{"href":5753,"rel":5754},"https:\u002F\u002Fdocs.omni.co\u002Ffinding-content\u002Fdrafting-publishing\u002Fbranch-mode.md",[2384],[5756],{"type":51,"value":5757},"Branch Mode",{"type":46,"tag":84,"props":5759,"children":5761},{"id":5760},"related-skills",[5762],{"type":51,"value":5763},"Related Skills",{"type":46,"tag":672,"props":5765,"children":5766},{},[5767,5776,5786],{"type":46,"tag":444,"props":5768,"children":5769},{},[5770,5774],{"type":46,"tag":67,"props":5771,"children":5772},{},[5773],{"type":51,"value":80},{"type":51,"value":5775}," — understand the model before modifying",{"type":46,"tag":444,"props":5777,"children":5778},{},[5779,5784],{"type":46,"tag":67,"props":5780,"children":5781},{},[5782],{"type":51,"value":5783},"omni-ai-optimizer",{"type":51,"value":5785}," — add AI context after building topics",{"type":46,"tag":444,"props":5787,"children":5788},{},[5789,5794],{"type":46,"tag":67,"props":5790,"children":5791},{},[5792],{"type":51,"value":5793},"omni-query",{"type":51,"value":5795}," — test new fields",{"type":46,"tag":5797,"props":5798,"children":5799},"style",{},[5800],{"type":51,"value":5801},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":5803,"total":2622},[5804,5814,5826,5838,5854,5868,5884,5892,5907,5919,5934],{"slug":5805,"name":5805,"fn":5806,"description":5807,"org":5808,"tags":5809,"stars":29,"repoUrl":30,"updatedAt":5813},"omni-admin","administer Omni Analytics","Administer an Omni Analytics instance — manage connections, users, groups, user attributes, permissions, schedules, and schema refreshes via the Omni CLI. Use this skill whenever someone wants to manage users or groups, set up permissions on a dashboard or folder, configure user attributes, create or modify schedules, manage database connections, refresh a schema, set up access controls, provision users, or any variant of \"add a user\", \"give access to\", \"set up permissions\", \"who has access\", \"configure connection\", \"refresh the schema\", or \"schedule a delivery\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5810,5811,5812],{"name":27,"slug":28,"type":16},{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},"2026-04-06T18:10:58.235995",{"slug":5815,"name":5815,"fn":5816,"description":5817,"org":5818,"tags":5819,"stars":29,"repoUrl":30,"updatedAt":5825},"omni-ai-eval","evaluate Omni AI query accuracy","Evaluate Omni AI accuracy using Omni's built-in eval system — define a prompt set, run a judged eval against a model (or branch), and read the accuracy-judge verdicts. Use this skill whenever someone wants to evaluate Omni AI, benchmark Blobby, run regression tests, compare AI output across branches or model-context changes, measure AI quality, run A\u002FB tests on model changes, assess the impact of an ai_context or modeling change, or any variant of \"run evals\", \"test Blobby\", \"benchmark query generation\", \"compare AI results\", \"regression test\", \"how accurate is the AI\", or \"measure the impact of my changes\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5820,5821,5824],{"name":27,"slug":28,"type":16},{"name":5822,"slug":5823,"type":16},"Evals","evals",{"name":14,"slug":15,"type":16},"2026-04-06T18:10:59.485921",{"slug":5783,"name":5783,"fn":5827,"description":5828,"org":5829,"tags":5830,"stars":29,"repoUrl":30,"updatedAt":5837},"optimize Omni models for AI","Optimize your Omni Analytics model for Blobby, the Omni Agent — configure ai_context, ai_fields, synonyms, sample_queries, and AI-specific topic extensions. Use this skill whenever someone wants to improve AI accuracy in Omni, make Blobby smarter, add AI context or example questions, curate which fields the AI sees, personalize AI context by user attribute, scope context to a model tier or agent, diagnose context-window pruning or truncation, control which topics AI can reach, troubleshoot why Blobby gives wrong answers, or any variant of \"make the AI better\", \"Blobby isn't answering correctly\", \"optimize for AI\", or \"teach the AI about our data\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5831,5834,5835,5836],{"name":5832,"slug":5833,"type":16},"AI Context","ai-context",{"name":27,"slug":28,"type":16},{"name":24,"slug":25,"type":16},{"name":14,"slug":15,"type":16},"2026-07-27T06:09:42.358967",{"slug":5839,"name":5839,"fn":5840,"description":5841,"org":5842,"tags":5843,"stars":29,"repoUrl":30,"updatedAt":5853},"omni-content-builder","manage Omni Analytics documents and dashboards","Create, update, and manage Omni Analytics documents and dashboards programmatically — document lifecycle, drafts, tiles, visualizations, filters, controls, and layouts — using the Omni CLI. Use this skill whenever someone wants to build a dashboard, create a workbook, add tiles or charts, configure dashboard filters or controls, update an existing dashboard's model, set up a KPI view, create visualizations, lay out a dashboard, arrange tiles or pages, create a document, edit a dashboard as a draft, publish a draft, change dashboard settings, rename a workbook, delete a dashboard, move a document to a folder, duplicate a dashboard, or any variant of \"build a dashboard for\", \"create a report showing\", \"add a chart to\", \"make a dashboard\", \"update the dashboard layout\", \"rename this document\", \"publish this draft\", \"move to folder\", or \"delete this dashboard\". Also use when modifying dashboard-level model customizations like workbook-specific joins or fields.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5844,5845,5846,5849,5852],{"name":27,"slug":28,"type":16},{"name":21,"slug":22,"type":16},{"name":5847,"slug":5848,"type":16},"Dashboards","dashboards",{"name":5850,"slug":5851,"type":16},"Data Visualization","data-visualization",{"name":14,"slug":15,"type":16},"2026-04-06T18:11:02.007785",{"slug":5855,"name":5855,"fn":5856,"description":5857,"org":5858,"tags":5859,"stars":29,"repoUrl":30,"updatedAt":5867},"omni-content-explorer","find and organize Omni Analytics content","Find, browse, and organize content in Omni Analytics — dashboards, workbooks, folders, and labels — using the Omni CLI. Use this skill whenever someone wants to find an existing dashboard, search for content, list workbooks, browse folders, see what dashboards exist, find popular reports, download a dashboard as PDF or PNG, favorite content, manage labels on documents, or any variant of \"find the dashboard about\", \"what reports do we have\", \"show me our dashboards\", \"where is the sales report\", or \"download this dashboard\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5860,5861,5862,5863,5864],{"name":27,"slug":28,"type":16},{"name":21,"slug":22,"type":16},{"name":5847,"slug":5848,"type":16},{"name":14,"slug":15,"type":16},{"name":5865,"slug":5866,"type":16},"Search","search","2026-04-06T18:11:04.516766",{"slug":5869,"name":5869,"fn":5870,"description":5871,"org":5872,"tags":5873,"stars":29,"repoUrl":30,"updatedAt":5883},"omni-embed","embed Omni Analytics dashboards","Embed Omni Analytics dashboards in external applications — URL signing, custom themes, iframe events, entity workspaces, and permission-aware content — using the @omni-co\u002Fembed SDK and Omni CLI. Use this skill whenever someone wants to embed a dashboard, sign an embed URL, customize the embedded theme, handle embed events, listen for clicks or drills in the iframe, send filters to an embedded dashboard, set up entity workspaces, look up embed users, build a permission-aware content list, white-label an embedded dashboard, or any variant of \"embed this dashboard\", \"customize the iframe theme\", \"handle click events from the embed\", \"filter the embedded dashboard\", \"set up embedding\", or \"what dashboards can this user see\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5874,5875,5876,5879,5880],{"name":27,"slug":28,"type":16},{"name":5847,"slug":5848,"type":16},{"name":5877,"slug":5878,"type":16},"Frontend","frontend",{"name":14,"slug":15,"type":16},{"name":5881,"slug":5882,"type":16},"SDK","sdk","2026-04-06T18:10:55.739544",{"slug":4,"name":4,"fn":5,"description":6,"org":5885,"tags":5886,"stars":29,"repoUrl":30,"updatedAt":31},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5887,5888,5889,5890,5891],{"name":27,"slug":28,"type":16},{"name":21,"slug":22,"type":16},{"name":24,"slug":25,"type":16},{"name":14,"slug":15,"type":16},{"name":18,"slug":19,"type":16},{"slug":80,"name":80,"fn":5893,"description":5894,"org":5895,"tags":5896,"stars":29,"repoUrl":30,"updatedAt":5906},"discover and inspect Omni Analytics models","Discover and inspect Omni Analytics models, topics, views, fields, dimensions, measures, and relationships using the Omni CLI. Use this skill whenever someone wants to understand what data is available in Omni, explore their semantic model, find specific fields or views, check how tables join together, see what topics exist, or asks any variant of \"what can I query\", \"what fields are available\", \"show me the model\", \"what data do we have\", or \"how is this data modeled\". Also use when you need to understand the Omni model structure before building or modifying anything.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5897,5898,5899,5902,5905],{"name":27,"slug":28,"type":16},{"name":21,"slug":22,"type":16},{"name":5900,"slug":5901,"type":16},"Data Analysis","data-analysis",{"name":5903,"slug":5904,"type":16},"Knowledge Management","knowledge-management",{"name":14,"slug":15,"type":16},"2026-04-06T18:11:00.739003",{"slug":5793,"name":5793,"fn":5908,"description":5909,"org":5910,"tags":5911,"stars":29,"repoUrl":30,"updatedAt":5918},"query Omni Analytics semantic layer","Run queries against Omni Analytics' semantic layer using the Omni CLI, interpret results, and chain queries for multi-step analysis. Use this skill whenever someone wants to query data through Omni, run a report, get metrics, pull numbers, analyze data, ask \"how many\" \u002F \"what's the trend\" \u002F \"show me the data\", retrieve dashboard query results, or extract data from an existing dashboard or workbook. Also use for table calculations and computed columns (running totals, percent-of-total, month-over-month \u002F period-over-period change, moving averages, rankings), open-ended multi-step analysis via agentic AI jobs, and running raw SQL through the semantic layer — even when the user doesn't say \"query\" (e.g. \"add a running total column\", \"what's our MoM growth\", \"analyze revenue trends\"). For building or editing a dashboard or chart use omni-content-builder; for adding a field or measure to the model use omni-model-builder — this skill retrieves and computes over data.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5912,5913,5914,5915,5916],{"name":27,"slug":28,"type":16},{"name":21,"slug":22,"type":16},{"name":5900,"slug":5901,"type":16},{"name":14,"slug":15,"type":16},{"name":5917,"slug":3424,"type":16},"SQL","2026-04-06T18:10:56.974412",{"slug":5920,"name":5920,"fn":5921,"description":5922,"org":5923,"tags":5924,"stars":29,"repoUrl":30,"updatedAt":5933},"omni-to-databricks-metric-view","convert Omni topics to Databricks Metric Views","Convert an Omni Analytics topic into a Databricks Metric View definition in Unity Catalog. Use this skill whenever someone wants to export Omni metrics to Databricks, create a Metric View from an Omni topic, harden BI metrics into Unity Catalog, or bridge Omni's semantic layer with Databricks AI\u002FBI dashboards and Genie spaces.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5925,5926,5929,5932],{"name":27,"slug":28,"type":16},{"name":5927,"slug":5928,"type":16},"Data Engineering","data-engineering",{"name":5930,"slug":5931,"type":16},"Databricks","databricks",{"name":14,"slug":15,"type":16},"2026-04-09T04:47:50.695014",{"slug":5935,"name":5935,"fn":5936,"description":5937,"org":5938,"tags":5939,"stars":29,"repoUrl":30,"updatedAt":5947},"omni-to-snowflake-semantic-view","convert Omni topics to Snowflake views","Convert an Omni Analytics topic into a Snowflake Semantic View YAML definition. Use this skill whenever someone wants to export Omni metrics to Snowflake, create a Semantic View from an Omni topic, harden BI metrics into the warehouse, or bridge Omni's semantic layer with Snowflake Cortex Analyst.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5940,5941,5942,5943,5946],{"name":27,"slug":28,"type":16},{"name":5927,"slug":5928,"type":16},{"name":14,"slug":15,"type":16},{"name":5944,"slug":5945,"type":16},"Snowflake","snowflake",{"name":18,"slug":19,"type":16},"2026-04-06T18:11:25.009706",{"items":5949,"total":2622},[5950,5956,5962,5969,5977,5985,5993],{"slug":5805,"name":5805,"fn":5806,"description":5807,"org":5951,"tags":5952,"stars":29,"repoUrl":30,"updatedAt":5813},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5953,5954,5955],{"name":27,"slug":28,"type":16},{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},{"slug":5815,"name":5815,"fn":5816,"description":5817,"org":5957,"tags":5958,"stars":29,"repoUrl":30,"updatedAt":5825},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5959,5960,5961],{"name":27,"slug":28,"type":16},{"name":5822,"slug":5823,"type":16},{"name":14,"slug":15,"type":16},{"slug":5783,"name":5783,"fn":5827,"description":5828,"org":5963,"tags":5964,"stars":29,"repoUrl":30,"updatedAt":5837},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5965,5966,5967,5968],{"name":5832,"slug":5833,"type":16},{"name":27,"slug":28,"type":16},{"name":24,"slug":25,"type":16},{"name":14,"slug":15,"type":16},{"slug":5839,"name":5839,"fn":5840,"description":5841,"org":5970,"tags":5971,"stars":29,"repoUrl":30,"updatedAt":5853},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5972,5973,5974,5975,5976],{"name":27,"slug":28,"type":16},{"name":21,"slug":22,"type":16},{"name":5847,"slug":5848,"type":16},{"name":5850,"slug":5851,"type":16},{"name":14,"slug":15,"type":16},{"slug":5855,"name":5855,"fn":5856,"description":5857,"org":5978,"tags":5979,"stars":29,"repoUrl":30,"updatedAt":5867},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5980,5981,5982,5983,5984],{"name":27,"slug":28,"type":16},{"name":21,"slug":22,"type":16},{"name":5847,"slug":5848,"type":16},{"name":14,"slug":15,"type":16},{"name":5865,"slug":5866,"type":16},{"slug":5869,"name":5869,"fn":5870,"description":5871,"org":5986,"tags":5987,"stars":29,"repoUrl":30,"updatedAt":5883},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5988,5989,5990,5991,5992],{"name":27,"slug":28,"type":16},{"name":5847,"slug":5848,"type":16},{"name":5877,"slug":5878,"type":16},{"name":14,"slug":15,"type":16},{"name":5881,"slug":5882,"type":16},{"slug":4,"name":4,"fn":5,"description":6,"org":5994,"tags":5995,"stars":29,"repoUrl":30,"updatedAt":31},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[5996,5997,5998,5999,6000],{"name":27,"slug":28,"type":16},{"name":21,"slug":22,"type":16},{"name":24,"slug":25,"type":16},{"name":14,"slug":15,"type":16},{"name":18,"slug":19,"type":16}]