Skip to main content
Contracts are split across four directories, each validated against its own Pydantic model at load time. See Contracts & guardrails for the concepts behind these fields. A finality rule is filename-discriminated from a guardrail: both live under contracts/guardrails/, but only files named finality-*.yaml load as FinalityRule. Everything else in that directory loads as Guardrail.

contracts/metrics/*.yaml: MetricBinding

A metric binding is what turns a name like revenue into something the compiler can actually run. metric is the name callers ask for, canonical is the recipe for computing it, and owner/aliases/examples exist to keep the binding discoverable and accountable rather than a bare formula nobody can trace back to a person or a prior query. status: deprecated lets you keep an old binding around for history without it competing for a name a newer binding now owns. Ambiguity rule: if a requested name matches zero or more than one active binding, the compiler returns a structured AMBIGUOUS/UNRESOLVED error rather than guessing.

canonical (CanonicalRef): one shape per kind

kind selects a compilation strategy, and each kind needs a different subset of the fields below. Missing a required field for the chosen kind raises a located error at load time rather than failing later at query time. Most metrics are single, a direct pointer at one measure on one source. The other kinds exist for shapes a plain source-and-measure pair can’t express on its own: ratio and weighted_avg combine two other metrics after they’re each aggregated independently, semi_additive handles point-in-time values like balances that can’t just be summed across every dimension, distinct_count and percentile recompute their value at whatever grain the query asks for instead of pre-aggregating, and opaque is the escape hatch for a measure that’s only trustworthy at the exact grain it was computed at. Full field list on CanonicalRef:

deprecated_alternatives[] (DeprecatedAlternative)

examples[] (Example)

contracts/guardrails/*.yaml: Guardrail

A guardrail attaches a rule to either a specific (source, measure) pair or a whole metric, chosen by which shape you fill in under applies_to. Which fields besides kind are required depends entirely on that kind: a mandatory_filter needs filter, a restrict_source needs restrict_to and context, a min_trust needs level and context, a required_dimension needs dimension. severity decides how hard the rule bites: for restrict_source, min_trust, and required_dimension, error stops the query outright while warn lets it through with a warnings[] entry instead. mandatory_filter has no “block” action to gate, its predicate is always injected, and severity: warn there only adds the warnings[] entry. rationale is what actually gets shown to whoever hit the block (or the warning), so it’s worth writing for a person, not just as a code comment.

contracts/guardrails/finality-*.yaml: FinalityRule

realizations[] (Realization)

A finality rule exists for a metric that can be served from more than one physical realization of the same data, typically a slower, fully-reconciled table and a faster, real-time one. realizations lists each candidate source along with its role, final or provisional, and coalescing is the expression that decides which realization actually backs a given time window in the result. board_only_final is the piece that ties this to a guardrail: pair it with a restrict_source guardrail so a sensitive context (board reporting, say) only ever sees the final realization, never the faster but not-yet-settled one.

contracts/assertions/*.yaml: Assertion

expect (AssertionExpect)

An assertion is a known-good query paired with the answer it should produce, usually sourced from a finance close or another trusted report. query is run through the exact same compiler path a real caller would hit, so an assertion tests the whole stack, not just the metric definition in isolation. source_of_truth matters more than it looks: it’s the thing that lets a reviewer trust the expected value in the first place, an assertion with no traceable origin is just a number someone typed in. canonic assert runs every assertion through the compiler and gates on the result. canonic query --harness runs matching assertions inline against a single query.

contracts/policies/tenancy.yaml: TenancyPolicy

See Tenancy & access control for the concepts behind this file. At most one per project. Its presence is the feature switch for row-level tenant isolation.

scoped_sources[] (ScopedSource)

Validation rules, enforced at load time with a file+line location:
  • scoped_sources and shared_sources must be disjoint: a source name appearing in both fails validation.
  • tenancy_for(source) is total over every source in the project once this file is present: a source reachable by a query but declared in neither list is Undeclared, governed by undeclared_source rather than treated as unrestricted.

contracts/policies/roles.yaml: RolePolicy

See Tenancy & access control for the two runtime gaps (dimensions enforcement, knowledge.allow_tags defaults) that matter when authoring this file.

roles{} (RoleDef): one entry per role name

AllowDenyPolicy (metrics/dimensions): allow: null (default) is unrestricted subject to deny. allow: [] is a deliberate allow-nothing. "*" in allow is the explicit wildcard. deny always wins over allow.

masking[] (MaskingRule)

Validation rules, enforced at load time with a file+line location:
  • default_role, if set, must name a role declared in roles.
  • inherits, if set, must resolve to a declared role and the resulting chain must be acyclic: a role that (directly or transitively) inherits from itself fails validation.
  • inherits is single-parent field-level override, not list-merge: a role that explicitly authors a field (even to an empty list) replaces the parent’s value for that field wholesale. A field left untouched inherits the parent’s value unchanged.