nuMetrix
DSL Naming
Conventions
How we name YAML definitions, compiled SQL models, contracts,
and everything in between
March 2026
Motivation
Why Naming Matters
- Predictability — Given a YAML filename, you can guess the compiled SQL model name without looking it up
- Globbing —
dbt build --select probe_findings__*does what you’d expect - DAG readability — In dbt’s DAG, you can see at a glance what layer and family a model belongs to
- Union coherence — The union view’s name hints at what its children are called
- Mechanical renaming — One convention means one search-and-replace when things change
The most important property of a naming convention is consistency. The second most important is that it fails loudly when violated.
nuMetrix
Act I
The Three Artefact Families
Probes, hypotheses, diagnoses — and how each one is named.
Prefixes
Identity Prefixes
| Family | Prefix | YAML Example | ID Field |
|---|---|---|---|
| Probe | probe_ | probe_revenue_leakage.yaml | probe_id: probe_revenue_leakage |
| Assessment | assessment_ | assessment_material_health.yaml | probe_id: assessment_material_health |
| Hypothesis | hyp_ | hyp_revenue_leakage_unbilled.yaml | hypothesis_id: hyp_revenue_leakage_unbilled |
| Diagnosis | diag_ | diag_billing_workflow_gap.yaml | diagnosis_id: diag_billing_workflow_gap |
Rule: the ID field must match the filename (without
.yaml). Enforced by hypothesischeck.py and diagnosischeck.py at compile time. Probes follow the same convention by practice.
Assessments
Assessments Are a Subtype of Probe
- Assessments use
probe_idas their identifier field — notassessment_id - Compiled output follows the
probe_findings__pattern, just like regular probes - The
assessment_prefix on theprobe_idvalue is what distinguishes them
Compiled model names
probe_findings__probe_revenue_leakage # regular probe
probe_findings__probe_duplicate_billing # regular probe
probe_findings__assessment_material_health # assessment
probe_findings__assessment_billing_quality # assessment
From the dbt DAG’s perspective, assessments are probes. The prefix is the only signal — and it’s enough for
--select probe_findings__assessment_*.
nuMetrix
Act II
The Double-Underscore Convention
How compiled SQL model names are built from YAML identifiers.
Pattern
The __ Pattern
Anatomy of a compiled model name
probe_findings__probe_revenue_leakage
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
output type artefact identity (= probe_id from YAML)
- Left of
__— what kind of table it is:probe_findings, conforming to the findings contract - Right of
__— the literal ID from the YAML definition, including its prefix - Formula —
f"probe_findings__{probe['probe_id']}"inprobecompile.py
The double underscore is a namespace separator. Single underscores appear within names (
revenue_leakage). Double underscores separate the output type from the instance identity. No ambiguity.
Probes
Probes: One File Per Artefact
probe_*.yaml
18 files
18 files
→
probecompile.py
→
probe_findings__*.sql
18 files (1:1)
18 files (1:1)
dbt/numetrix/models/probes/
probe_findings__probe_revenue_leakage.sql
probe_findings__probe_duplicate_billing.sql
probe_findings__probe_orphan_billing.sql
probe_findings__probe_stale_article.sql
probe_findings__assessment_material_health.sql
probe_findings__assessment_billing_quality.sql
... (18 models total)
Each probe compiles to its own dbt model. The union view
platform_probe_findings aggregates them via UNION ALL BY NAME. Naming invariant: the union view’s prefix matches its children.
Verdicts
Hypotheses & Diagnoses: Monolithic Files
Hypotheses
hyp_revenue_leakage.yaml
hyp_stale_catalogue.yaml
hyp_duplicate_billing.yaml
↓ hypothesiscompile.py
hypothesis_verdicts.sql (one file)
hypothesis_registry.sql (one file)
hyp_stale_catalogue.yaml
hyp_duplicate_billing.yaml
↓ hypothesiscompile.py
hypothesis_verdicts.sql (one file)
hypothesis_registry.sql (one file)
Diagnoses
diag_billing_workflow_gap.yaml
diag_catalogue_maintenance_lag.yaml
diag_duplicate_interface_error.yaml
↓ diagnosiscompile.py
diagnosis_verdicts.sql (one file)
diagnosis_registry.sql (one file)
diag_catalogue_maintenance_lag.yaml
diag_duplicate_interface_error.yaml
↓ diagnosiscompile.py
diagnosis_verdicts.sql (one file)
diagnosis_registry.sql (one file)
Unlike probes, hypotheses and diagnoses compile N YAML files into one monolithic SQL file. All hypothesis CTEs are UNION ALL’d inside a single
hypothesis_verdicts.sql. No __ separator because there are no per-artefact files.
Design
Why the Asymmetry?
Probes → One File Each
Probes are independent. Each queries Gold entities directly. No inter-probe dependencies (except assessments). Adding a probe = adding a file.
dbt build --select probe_findings__probe_X rebuilds just one.
Verdicts → Monolithic
Hypotheses reference multiple probe findings tables. Diagnoses reference hypothesis_verdicts + probe findings. A single model with all CTEs lets dbt resolve the full dependency graph in one pass.
The trade-off: monolithic files are harder to selectively rebuild, but they avoid a combinatorial explosion of dbt
ref() cross-dependencies. The naming convention adapts to the compilation strategy, not the other way around.
nuMetrix
Act III
Layer Prefixes & Contracts
Medallion layer naming and the contract versioning scheme.
Layers
Layer Prefixes
A material row flows through the pipeline
bronze_materials → silver_materials → gold_materials → platform_gold_materials
| Prefix | Layer | Materialization | Example |
|---|---|---|---|
| bronze_ | Bronze | TABLE | bronze_cases, bronze_billing_events |
| silver_ | Silver | TABLE | silver_cases, silver_billing_events |
| gold_ | Gold | VIEW | gold_cases, gold_taxonomy_closure |
| platform_gold_ | Platform | VIEW | platform_gold_cases |
| platform_ | Platform (DSL) | VIEW | platform_probe_findings, platform_hypothesis_verdicts |
The entity name (
cases, billing_events, materials) is invariant across all layers. Change the prefix, keep the noun.
Contracts
Contract Naming
| File | Version Key | YAML Reference | Who Uses It |
|---|---|---|---|
| gold_contract.v1.json | gold.v1 | contract: "gold.v1" | Most probes |
| silver_contract.v1.json | silver.v1 | contract: "silver.v1" | Silver audit probes |
| findings_contract.v1.json | findings.v1 | contract: "findings.v1" | Assessments |
| diagnosis_contract.v1.json | diagnosis.v1 | contract: "diagnosis.v1" | Diagnosis compiler |
Pattern:
{layer}_contract.v{N}.json. The version key inside the JSON uses the short form (gold.v1). YAML definitions reference this short key. Bumping to v2 = new file, old probes still compile against v1.
Entities
Entity Names in Contracts
Contract Entity (PascalCase)
BillingEvent
CaseMaterialUsage
Case
Material
Procedure
CaseMaterialUsage
Case
Material
Procedure
dbt Model (snake_case)
gold_billing_events
gold_case_material_usage
gold_cases
gold_materials
gold_procedures
gold_case_material_usage
gold_cases
gold_materials
gold_procedures
Probes reference
BillingEvent (PascalCase, domain language). The contract maps it to gold_billing_events (snake_case, dbt convention). resolve_ref() bridges the gap. If a dbt model renames, update one JSON field — all probes recompile correctly.
nuMetrix
Act IV
The Full Naming Pipeline
From YAML file to platform union view — following one name through the system.
Probe
Probe: End-to-End
Following a probe through the naming system
1. YAML definition
probes/probe_revenue_leakage.yaml
probe_id: probe_revenue_leakage # must match filename
contract: "gold.v1" # references gold_contract.v1.json
2. Compiled dbt model
models/probes/probe_findings__probe_revenue_leakage.sql # one file per probe
3. Per-tenant table (via generate_schema_name)
hospital_alpha.probe_findings__probe_revenue_leakage
4. Platform union view
platform.platform_probe_findings # UNION ALL BY NAME across tenants
5. Registry entry
platform.platform_probe_registry # tri-lingual display text
Hypothesis
Hypothesis: End-to-End
Following a hypothesis through the naming system
1. YAML definition
hypotheses/hyp_revenue_leakage_unbilled.yaml
hypothesis_id: hyp_revenue_leakage_unbilled # must match filename (enforced)
2. Compiled dbt model (monolithic — no __ separator)
models/probes/hypothesis_verdicts.sql # all hypotheses in one file
# Inside: CTEs named hyp_revenue_leakage_unbilled__evidence,
# hyp_revenue_leakage_unbilled__scored,
# hyp_revenue_leakage_unbilled__verdict
3. Per-tenant table
hospital_alpha.hypothesis_verdicts # one row per hypothesis
4. Platform union view
platform.platform_hypothesis_verdicts # UNION ALL across tenants
Diagnosis
Diagnosis: End-to-End
Following a diagnosis through the naming system
1. YAML definition
diagnoses/diag_billing_workflow_gap.yaml
diagnosis_id: diag_billing_workflow_gap # must match filename (enforced)
hypothesis_id: hyp_revenue_leakage_unbilled # must reference existing hypothesis
2. Compiled dbt model (monolithic — no __ separator)
models/probes/diagnosis_verdicts.sql # all diagnoses in one file
3. Per-tenant table
hospital_alpha.diagnosis_verdicts # rows only for confirmed hypotheses
4. Platform union view
platform.platform_diagnosis_verdicts # UNION ALL across tenants
Note the cross-family reference:
diagnosis_id: diag_billing_workflow_gap points to hypothesis_id: hyp_revenue_leakage_unbilled. Prefixes (diag_, hyp_) make the reference direction unambiguous.
nuMetrix
Act V
Design Decisions
The trade-offs we considered and why we chose this way.
Trade-off
Why {family}__{instance}?
We considered three alternatives for verdict model names:
| Option | Example | Trade-off |
|---|---|---|
| Main noun prefix | verdict__hyp_X | Both families share verdict__*. Union view name diverges from children. |
| Family prefix | hypothesis_verdicts__hyp_X | Consistent with probes. Union view matches children. Verbose but predictable. |
| Bare ID | hyp_X | Cleanest, but loses the “this is a verdict” signal in the DAG. |
We chose family prefix because the union view’s name (
hypothesis_verdicts) naturally matches its children (hypothesis_verdicts__hyp_*). --select hypothesis_verdicts__* does what you’d expect. Same pattern as probes.
Registries
Registry Naming
Per-Tenant (in models/probes/)
probe_registry.sql
hypothesis_registry.sql
diagnosis_registry.sql
hypothesis_registry.sql
diagnosis_registry.sql
Platform (in models/platform/)
platform_probe_registry.sql
platform_hypothesis_registry.sql
platform_diagnosis_registry.sql
platform_hypothesis_registry.sql
platform_diagnosis_registry.sql
Registries contain tri-lingual display text (DE/EN/FR). Content is tenant-independent — identical across all tenants. Platform views read from the first tenant only, unlike findings/verdicts which union across all.
Summary
The Complete Naming Map
| Family | YAML Dir | Prefix | Compiled SQL | Strategy | Platform View |
|---|---|---|---|---|---|
| Probe | probes/ | probe_ | probe_findings__probe_* | 1 file each | platform_probe_findings |
| Assessment | probes/ | assessment_ | probe_findings__assessment_* | 1 file each | (same union) |
| Hypothesis | hypotheses/ | hyp_ | hypothesis_verdicts | monolithic | platform_hypothesis_verdicts |
| Diagnosis | diagnoses/ | diag_ | diagnosis_verdicts | monolithic | platform_diagnosis_verdicts |
One convention, one search-and-replace. Given any YAML file, you can derive every downstream name mechanically. That’s the whole point.
nuMetrix
nuMetrix