canonic ingest → review → apply, a real schema change and how canonic reacts to it, defining a canonical metric and a guardrail by hand, and the same loop running headless in CI.
Every command below and every diff shown is real output from a live run against a copy of examples/rental, not hand-written.
The loop, once
A fresh project, bootstrapped:semantics/ yet. It’s a proposal. Walk through them:
canonic apply <run_dir> is the batch alternative: apply everything still pending in one shot, useful scripted or after hand-editing a diff file yourself.
When the data actually changes
Six months later, someone adds a column: the rental company starts tracking goodwill repair-cost waivers.canonic ingest again (not --bootstrap, this project is already onboarded):
source_fingerprint, so nothing is proposed beyond a last_validated_at refresh, no_op, per the reconciliation decision table. damages did change, so it’s an edit, not a silent rewrite:
sum measure for it too, confidence: 1.0. It’s still propose-only: this project’s canonic.yaml has reconcile.auto_apply.enabled: false (the default), and structural fields like new measures never auto-apply regardless of confidence. canonic review shows the same diff and prompt as before. Accept it, and damages.yaml now has waived_amount and total_waived_amount.
If
damages.yaml had instead been human_curated (promoted with canonic review curate after the wizard, as the tracked rental example is), this same run would not propose a clean edit. A curated file always outranks new inferred evidence. Reconciliation flags a contradiction and leaves the file untouched, for every table whose drafted shape no longer matches the curated one, not just the changed table. See provenance tiers.waived_amount is a real, queryable measure, but a raw (source, measure) pair, not yet a canonical metric.
From auto-drafted to governed
This is the “a bit more manual effort” half quickstart mentions but doesn’t show. Semantics is auto-maintained. Deciding what’s canonical and mandatory is deliberately human-owned:canonic ingest. You write them, commit them, review them like any other code change. That’s the split rule from the three layers: semantics tracks physical reality, and contracts decide what’s authoritative.
Prove it
--json shows the resolution and the guardrail actually firing:
mandatory_filter is AND-ed into the compiled SQL whether or not the caller asked for it. An MCP-connected agent resolves the exact same binding and guardrail on its next call: CLI and MCP dispatch to the same core service, there’s no separate “agent view” to keep in sync.
canonic audit’s onboarding funnel also now shows the curated-review milestone reached:
The earlier funnel steps only light up when they’re driven through the interactive
canonic setup wizard: this walkthrough called canonic ingest/query directly, so only the review milestone fired. Following Quickstart first fills in the rest.The same loop, headless
Everything above works unattended. Headless mode is explicit--headless, or auto-detected from CI=true:
canonic apply batches these in without a review prompt, matching how a CI job would consume them.
The same data-change scenario, headless:
--open-pr/--no-pr to force either way). It needs an actual git remote to push to, which a scratch directory doesn’t have. --strict is the CI gate: any flagged contradiction fails the run with exit code 14, instead of silently proceeding.
A minimal scheduled-ingest job looks like:
Recap
Same design principle throughout: canonic proposes, you approve.