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_sourcesandshared_sourcesmust 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 isUndeclared, governed byundeclared_sourcerather 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 inroles.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.inheritsis 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.