Documentation Index

Fetch the complete documentation index at: https://docs.ursahealth.com/llms.txt

Use this file to discover all available pages before exploring further.

Data Model

Prev Next

The Data Model page is a read-only browser of the Ursa data model. It shows which objects, measures, and prebuilt diagnostic definitions exist in each analytics module, and lets you drill into the build health of any individual object or measure. Reach it from the Data Model item in the Compass sidebar.

The Module Tree

Modules are laid out as a tree, in three sections from top to bottom:

  • Analytics Modules — the canopy. Modules that produce datamarts or measures: the analytic output of the data model.
  • Core Data Model — the trunk: the Ursa core data model.
  • Integration Modules — the roots. Modules that hold source data, semantic mappings, and local transforms: the raw material feeding everything above.

A module that both integrates source data and produces analytic output appears twice, once in the canopy and once in the roots. Within each section, modules are listed alphabetically.

Each module appears as a collapsed box showing its name, its description, and a summary line counting what it contains — for example, 3 diagnostics, 12 measures, 2 datamarts, 41 other objects. Datamarts are counted separately from other objects so it's easy to see at a glance which modules publish analytic tables.

Filtering

The filter box at the top of the page narrows all three sections at once. It matches:

  • Objects on name, description, and table name.
  • Measures on identifier, nickname, name, and the measure, numerator, and denominator descriptions.
  • Diagnostics on name, description, and domain.

Filtering happens before modules are grouped, so a module with no matching contents disappears from the tree entirely.

Module Contents

Click a module box to expand it. Up to three labeled sections appear.

Diagnostics

Prebuilt diagnostic definitions that reference this module's objects or measures. Each row shows the definition's domain as a badge, plus its name and description. Clicking a diagnostic opens the definition in a new tab. A definition that spans several modules appears under each of them. Diagnostics in the Training domain are not listed.

Measures

Each measure is listed by identifier, with its nickname and description. Click a measure to open its Lineage Health report.

Objects

Each object shows a layer badge — Data Mart, Synthetic Object, Natural Object, Semantic Mapping, Local Transform, or Source Data — along with its name, description, and table name. Objects are sorted with the most derived layers first, so datamarts appear above natural objects, which appear above source data. Dedicated precursors are not listed on their own; they are folded into the terminal object they support. Click an object to open its Lineage Health report.

Lineage Health

Clicking an object or a measure replaces the tree with a Lineage Health report for that item. The focus is held in the page URL, so the report can be linked to directly and the browser's Back button returns you to the tree, as does the sidebar's Data Model link.

The report answers two different questions, split across two tabs that sit above the focus card:

  • ETL Validation — did the most recent ELT run cleanly for this object and everything upstream of it?
  • Data Freshness — is the data inside those objects as recent as it is supposed to be?

The two tabs each carries a status chip naming what it found: green ✓ when everything it checks is within bounds, yellow ⚠ when the only outstanding checks are warnings, red ✗ when something has failed, and grey when there is nothing to check. The chip counts what it is reporting — 4 failed validations, 2 validation warnings, 1 expectation unmet — so the tab you are not looking at can be read without opening it.

ETL Validation

This tab reads as a vertical pipeline that flows upward, matching the roots-to-canopy orientation of the module tree. Upstream namespace cards are stacked below the focus card, each module appearing below the modules that depend on it, ending with the source namespaces at the bottom. Labeled flow arrows between cards point up to show which modules feed which.

Source namespaces start collapsed. A collapsed card summarizes its contents — the freshest build age, how many objects are stale, and the validation tally as N pass / N handled / N warn / N fail. Click a card header to expand it into a list of its objects, sorted with the most derived layers first.

The tab's status chip counts the unresolved checks: red with the number of failures when any check is an unresolved error, yellow with the number of warnings when warnings are all that is outstanding, green when checks ran and none is outstanding, and grey before the results arrive or when there are no checks at all. Build freshness plays no part in it — an object staler than the objects feeding it says nothing about whether its validation rules passed, so a stale lineage whose checks all pass reads green here. The stale badges below say what the chip deliberately does not.

Build Freshness

This is freshness of the build — how long ago the table was last rebuilt. It is a different question from the Data Freshness tab, which is about how recent the data inside the table is. A table rebuilt an hour ago from a source file that stopped updating last month is fresh here and stale there.

Each object carries a freshness badge showing the age of its last successful build: Today, 1d, Nd, or No build.

The badge is neutral by default. It turns red only when the object is staler than its freshest upstream ancestor — meaning something it depends on has been rebuilt more recently and the object may be serving stale data. Hovering a red badge names the ancestor responsible, and how many other ancestors are also fresher.

A separate red fail Nd badge appears when the most recent failed build is newer than the most recent successful one, indicating the object's last build attempt did not succeed.

Validation

Alongside the freshness badge, each object shows one icon per validation check from its most recent build:

  • ✓ — the check passed.
  • ⚠ — the check raised a warning.
  • ✗ — the check failed.

Hover any icon to open a panel with the check's summary, or the validation rule itself when no summary is available. Every check gets the same panel; only what it holds differs.

Sentinel Issues

A warning or error raised by a saved ETL run is also tracked as a Sentinel issue, which carries the triage work done on it. Hovering such an icon shows the issue's identifier, its current status, who it is assigned to, and — once it has been resolved — the resolution reason. When the issue has been merged into another as a duplicate, the panel reports the state of the issue it was merged into, since that is where the work is being tracked.

The icon itself reflects how far that triage has gotten:

  • A green ⚠ or ✗ means the issue was resolved as a false positive, accepted, or rule tuning: the check fired, but the data turned out to be fine. The glyph is kept so the history stays visible, and the check counts toward N handled rather than N warn or N fail in the collapsed namespace tally.
  • A dimmed icon means the issue is closed for some other reason — resolved by a code change or a data resupply, suppressed, or merged. The problem was real and has been dealt with, so the icon keeps its warning or error color and still counts toward N warn or N fail.
  • A full-strength warning or error icon means the issue is still open, or the check is not tracked as an issue at all.

Precursors

Objects built from dedicated precursors show a ▸ Np toggle, where N is the number of precursors. Expanding it lists each precursor and its validation icons. Because precursor checks are also rolled up into the terminal object's icon row, a rolled-up check names its source precursor in its hover panel so a failure can be traced back to the right object.

Focus Measure

When the focus is a measure, the top card shows the measure's identifier, nickname, and description, followed by a Report Instances list. Each row names a report the measure is instantiated in, with a readiness icon — ✗ when the report failed, ⚠ when it is not yet ready, ✓ otherwise — and a badge for how recently it was updated.

Data Freshness

This tab reports how recent the data is, using the summary values each ELT run records according to the global Data Summary Tracking configuration in Ursa Studio. Only objects that map summary tracking fields record anything, so a typical lineage has a handful of tracked objects rather than all of them. When none of them do, the tab's chip is grey and the panel says so.

Each tracked object is shown at its most recent build at or below the focus object's most recent build. An upstream object rebuilt after the focus object was built holds data the focus object has not consumed yet, so reporting it would overstate what the focus object actually has. When the focus object has no recorded build, there is no ceiling to apply: each tracked object falls back to its own most recent build, and a note at the top of the tab says so.

Reading a measure

A measure is often not built by an ELT. It is calculated when its report is instantiated, so it has no ELT build record of its own to use as a ceiling. Two things follow.

The instant it was read at is the creation of the most recent report instance carrying it — the same timestamp the rest of Ursa Studio means by "when was this report instantiated". A measure live on several reports at once is read at the most recent of them. Only object builds that finished before that instant are eligible, because a build that finished afterwards is one the measure could not have seen.

The object it is read through is one of the objects the measure's own SQL selects from, chosen automatically and named in a note at the top of the tab. From there the tab behaves exactly as if that object were the focus: it supplies the build ceiling, and the objects listed under Tracked Per are the ones feeding it.

Picking one is a deliberate simplification. A measure typically reads from several base objects, and each of them saw a different state of the model; walking all of them would report a shared upstream object once per path, at a different build each time, with no way to reconcile the readings. The note names the base objects that were not chosen, so what is missing from the picture is visible rather than silent.

The choice goes by layer first and staleness second:

  • A Data Mart object wins, even against a staler sibling. These are the richest objects and usually the true foundation of the model, and their lineage tends to already contain what their siblings read.
  • A Metadata and Integration object is taken only when there is nothing else. Calendar dates, value sets, and reference objects are stale by design, and reading a measure through one would show almost nothing upstream of it.
  • Within a layer the stalest object wins, since a measure can be no fresher than its inputs and that is the reading that cannot overstate.

Two cases leave nothing to read through, and each gets its own note: a measure that has never been instantiated has no instant to read at, and a measure none of whose inputs had been built by the time it ran has no eligible object. Both fall back to each tracked object's own most recent build.

Did the data actually arrive?

Being at or below the focus object's build is not the same as having reached it. If the focus object was built in the same run that produced a tracked object's values, it consumed them — objects build in dependency order within a run, so the upstream object was written first. If it was not built in that run, the data had to travel down through the objects in between, and those may not have rebuilt since.

Consider a focus object fed by an intermediate object, itself fed by a source. One run rebuilds the source alone; a later run rebuilds the focus object alone. The source's newer values sit below the focus object's build and are duly reported — but the focus object was built from the intermediate, which has not run since, so it holds none of them.

Tracked objects in that position are marked with a in the Tracked Per list. Hovering it names the build the values came from and explains that the hand-off was not verified. The mark means unconfirmed, not wrong: most of the time the data did get through, and confirming it either way would mean replaying every build of every object in between.

The mark also appears when the values come from a build far enough back that it falls outside the recent history the report examines. Values that old are stale by any measure, so the prompt still applies.

Under a measure focus the same question is asked of the object the measure is read through, and the hover panel names that object rather than the measure — a measure was never built, so it cannot have been built in any particular run.

The tab's status chip is unaffected: it reports whether the configured expectations pass, and an unverified hand-off is not an expectation failure.

Tracked Per

A box on the right lists every object in the lineage that recorded summary values, with a checkbox each. All are checked to begin with. Unchecking narrows the numbers to the objects still checked; with a single object checked you are looking at that object's values alone.

When several objects are checked, their values are combined by taking the latest of the maximums and the earliest of the minimums. For example the later of two max data effective dates is the answer to "how recent is the data anywhere in here".

While a totalled field is selected the list becomes a single choice rather than a set of checkboxes, with a note saying so. Clicking a different object moves the selection to it. See Totals below for why.

Aggregate Field

Below the Tracked Per box, a radio button picks which aggregate field to show — one at a time, defaulting to Data Effective Date. A field configured as max or min shows a single value; one configured as min and max shows a range; one configured as sum or distinct shows a total.

Fields whose values are text are not offered, since text has no freshness reading and no threshold can be written against it. A distinct count of a text column is offered, though — counting distinct member ids is the ordinary case, and the count is a number whatever it counted.

Totals

A maximum is a position in an ordering, so several of them combine by taking the extreme. A total is not: it is a figure about one particular set of rows. Two objects' row counts added together count nothing real, and the larger of the two is not the count of anything either. So a sum or distinct field is greyed out whenever more than one object is checked, and hovering it explains that Tracked Per has to be narrowed to a single object first. Once one is selected, Tracked Per stays a single choice for as long as that field is showing.

Narrowing to one object settles who the total belongs to, but not the grain. ELT records one row per combination of every partition an object maps, so a Source row over an object that also maps Partner covers one recorded value per partner even with a single object checked. Sums simply add: the recorded rows divide the data up without overlapping, so the Source figure is the true total for that source. Distinct counts cannot be recovered that way — adding them counts a member who appears under two partners twice, and taking the largest undercounts. Rather than show a number that is not the distinct count of anything, such a row shows a dash with an explanation behind it, and the counts appear as recorded once the row is expanded far enough to reach them.

A dash of that kind is grey, not red: there is no figure, so no expectation is judged there. Expectations still fire at the grains that do have one, and a failure below carries up to the row, the section, and the tab's chip as usual.

Sources and Partitions

The main panel shows one section per partition in the configuration that has values recorded against it — typically Source ID and whatever else has been configured, such as a partner identifier. Each section lists its distinct values with the combined figure for each.

Any row can be expanded into the next partition on the section's drill path, and its children can be expanded again, until every partition on the path is pinned. By default a section drills by its own partition first and then the rest in configured order, so the Source section reads Source → Partner → Payer and the Partner section reads Partner → Source → Payer.

Drilling all the way down is what makes an expectation traceable. The deepest level pins every partition, so every expectation is evaluated on some row, and a red row can be followed down to the exact combination responsible instead of only telling you that something below it is wrong.

Expand a row in a section with more than one partition below its root and a set of drill controls appears in the section header: one dropdown per level, each offering the partitions not already spent higher up. Nothing is shown until then — a choice about the third level is no use to a reader who has not yet asked for a second. Collapse everything again and the controls go away, unless you have changed the path; a changed path keeps them on screen so the change stays visible and reversible. Point a level at a different partition to go straight there — a source's payers without walking through its partners first — or set a level to to stop the drill at that depth. Repointing a level rebuilds the levels below it in the default order. A shortened path is a real trade: the grains it no longer reaches are not evaluated in that section, so an expectation written below the cut goes grey there rather than being judged against a coarser number. It is still caught in the sections that do reach it.

Rows whose partition value is missing are grouped under — not tracked — rather than being dropped. This is what an object that records summary values but does not map that particular partition looks like, so a gap in coverage stays visible instead of silently disappearing.

Expectations

Values are colored against the expectations set in the Data Summary Tracking configuration:

  • Green — every expectation that applies to that value passes.
  • Red — at least one fails. Hovering the value explains which, in the terms the expectation was written in.
  • Grey — no expectation applies. Grey is not a pass; it means nobody has said what this value should be.

Expectations on date fields are written in days stale, so a maximum Data Effective Date is judged on its age rather than compared to a date.

An expectation scoped to a particular partition is only checked where that partition is pinned. A rule about one partner cannot be judged on a source row that covers every partner, so it is evaluated at the partner grain instead — and if it fails there, the source row above it turns red too. A collapsed row is therefore red whenever anything underneath it is, and the tab's chip is red whenever any row is. Expanding a red row down the drill path leads to the row that actually failed.

The chip counts expectations, not failing values. One rule can fail at many grains, and every section shows the same recorded values from a different root, so counting the red cells would report a single problem several times over. 1 expectation unmet means one configured rule is not being met somewhere, however many rows are red because of it. Narrowing Tracked Per or shortening a drill path can lower the count, since a rule that is never reached is never judged.

Bookmarks

A configured box might be worth keeping. Getting back to payer coverage on the claims lineage, tracked per the two staging objects, drilled to partner otherwise means navigating to the object, re-checking the same boxes, re-picking the field and re-expanding the same rows.

The bookmark icon beside a section title saves the box as it stands: the object in focus, which partition the section is rooted at, the Tracked Per selection, the aggregate field, the drill path, and which rows are expanded. The icon fills in to confirm, then retires. Bookmarking is one-way from here — a saved box is removed from the bookmarks page, not by clicking the icon again.

Only an object can be bookmarked.

When anything is bookmarked, the Data Model page's header carries a N data summaries bookmarked link. The page it leads to rebuilds each saved box against current data — a bookmark is a way to check the same reading again next week. Rows can be expanded and collapsed there, starting from where the bookmark was taken, but Tracked Per, the aggregate field and the drill path are fixed: they are what was bookmarked. Each card links back to its object on the Data Model page, where a different reading can be built and bookmarked in turn.

A bookmark whose object has left the data model, or whose partition or aggregate field is no longer recorded, says so in place of the box and can still be deleted. Bookmarks are per user; they are not shared.

Relationship to the AI's Data Model Tools

The Data Model page and the AI's data model tools read the same underlying model, but the page shows more of it. It lists objects at every layer, including source data and semantic mappings, and lists every measure whether or not it has been instantiated in a report. The AI's SEARCH_DATA_MODEL tool is deliberately narrower, returning only the analytic layers and only measures that are available to query. In the layers it covers this page is closer to FULL_SEARCH_DATA_MODEL, which reaches down the whole model rather than stopping at the analytic layers — though the filter box here matches on names and descriptions only, where that tool also searches full definitions and revision comments. See the Tools Reference for what the AI can retrieve during a session.

Access Control

  • Authenticated users can view the Data Model page and both tabs of its Lineage Health reports.
  • The page is read-only. There is nothing to create, edit, or delete, and no configuration controls which objects or measures appear. The Data Freshness tab reads the global Data Summary Tracking configuration but cannot change it; that is edited in Data Studio by an architect.
  • The view is global to the Ursa Studio instance. It is not scoped by diagnostic, session, or entity.