YAML is the primary surface. The app’s wizard is a fast path to a common shape, and the app can also take a suite as pasted YAML — but everything the product can do is expressible in the file, and some things are only expressible there. What needs YAML is the honest map.
Anatomy of a suite
suite.yaml
Configuration reference
Every field, rendered and browsable.
config.schema.json
The machine-readable schema, for editors and validators.
The smallest useful suite
A connection pair and one reconciliation:suite.yaml
Choosing the dataset
Each side of a reconciliation is a dataset: a connection plus what to read.- table (preferred)
- query
table: the tool resolves the column list for you, which means it can compare columns you did not have to enumerate, warn when the two sides disagree on shape, and narrow the comparison without you writing SQL:columns and exclude_columns are mutually exclusive. The table reference accepts a dotted string (db.schema.table) or a structured object with database, schema and name.Key columns decide everything
key_columns: (or key_column: — both accept a string or a list) names the column tuple that identifies a row. It is the single most consequential choice in a suite, because the drill-down orders and range-filters on it.
Two rules:
- It must be unique. A non-unique key makes segment boundaries ambiguous and the drill-down unreliable.
- It should be indexed. Each drill-down level range-filters on the key. With a matching primary key or index, the database prunes; without one, every segment is a full scan.
check-config --dbtells you which you have.
key_column; several as a key_columns list. That is only a formatting detail of synq-recon suite yaml output — write whichever you prefer.
Matching columns across the two sides
Column names that differ only in case match automatically, souser_id on Postgres lines up with USER_ID on Snowflake with no configuration. For genuinely different names, map them:
case_insensitive: false to turn the automatic behaviour off entirely.
Keep credentials out of the suite
Move the wholeconnections: block into a separate file, git-ignored, keyed by the same connection names:
.connections.yaml
.connections.yaml or connections.yaml in the working directory; --connections <path> points at one explicitly. Commit a .connections.yaml.example with ${ENV_VAR} placeholders so the next person knows which names to fill in.
Anywhere in a suite, ${VAR} reads an environment variable and ${VAR:-default} supplies a fallback.
Setup and teardown
SQL to run before and after the comparisons, per connection. Useful for staging a snapshot, materialising a view, or building fixtures in a test suite:setup_file and teardown_file take a path per connection instead of inline statements. Both blocks also exist per reconciliation, where teardown_on_failure and ignore_setup_errors inherit the suite’s value unless you set them.
ignore_setup_errors: true is right for idempotent bootstrap SQL (CREATE TABLE IF NOT EXISTS) and wrong when the setup is what produces the data being compared — you would be comparing two empty tables and calling it a match.Annotations
Name/value labels on a suite or a single reconciliation. Reconciliation-level annotations merge with the suite’s, and they follow a promoted suite into the platform, where they annotate the resulting assets and checks.Validate before you spend a query
Three steps, cheapest first. Do not skip ahead; each one catches a class of problem the next would surface as a confusing mid-run error.1
Parse and sanity-check — costs nothing
Configuration check passed!.2
Check the wiring
connection: values are workspace integration ids rather than local definitions, offline validation reports no connections defined — this is the step that tells you why.3
Check against the databases
LIMIT 0 — no scan, no rows. It reports the resolved columns, table size, primary and partition keys, a suggested key column, and a warning when the key column you chose is not indexed.Move on when every connection is OK and every query validates. A failure here is a wrong table name, a missing grant, or a column that does not exist.On BigQuery, the table analysis in
--db enumerates dataset metadata rather than jumping to the tables you named, so its cost scales with the size of the warehouse rather than the size of your suite. That is why the analysis is confined to this interactive command and does not run before every comparison.What the wizard covers and what needs YAML
In the app, Health → Reconciliations → Development has a new-suite wizard with four steps — Data Sources, Comparison, Suite Info, Review — and an Upload Suite Config dialog that takes a suite as pasted or uploaded YAML. The wizard is quicker for a first suite and better at discovery: it searches your catalog, so you can pick a table you already have rather than typing warehouse coordinates, and it shows which columns are keys or indexed while you choose. It covers a deliberate subset of the format:
Two capabilities exist only in the app, and are worth knowing about:
- Authoring from the catalog. The entity picker resolves a table you have already catalogued to its warehouse coordinates. From the CLI you supply coordinates yourself.
- Execution-plan preview. The app renders the resolved plan for a stored suite before you run it.
A complete example
Two reconciliations over the same pair of systems, in two different modes — row-level checksums for orders, and grouped totals for inventory, where row-level comparison would be the wrong instrument:ecommerce.yaml
Next
Comparison modes
Which mode to use, aggregate measures, and threshold resolution.
Time windows and cutoffs
Comparing two live systems without inventing differences.
Running locally
Filters, concurrency, JSON output, audit logs and exit codes.
Workspace suites
Save it, run it on our backend, promote it to production.