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 (hand-edited 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 hand-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; 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 report’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
| Step | Command | Docs |
|---|---|---|
| Draft proposals from live schema | canonic ingest | CLI reference · Concept |
| Walk through them one by one | canonic review | CLI reference |
| Batch-accept a run | canonic apply | CLI reference |
| Ask a question | canonic query | CLI reference |
| Declare what’s canonical / mandatory | contracts/metrics/*.yaml, contracts/guardrails/*.yaml | Concept |
| Check project state | canonic status / canonic report | CLI reference |