Skip to content

SQL Yodeler

llms.txtlists every page for an agent

SQL YODELER

Declarative schema migrations for ClickHouse and Postgres

Declare what the database should be, in TypeScript. yodel works out the change and applies it, or writes it as a migration, behind an approval of the exact plan.

src/schema.ts
export const events = table`
CREATE TABLE ${db}.events (
id UInt64,
kind LowCardinality(String),
at DateTime,
country LowCardinality(String) DEFAULT ''
)
ENGINE = MergeTree
ORDER BY (kind, at)`;
npx yodel new add-country, then npx yodel plan dev
20261010T2213-add-country
0 statement SQLCH201 metadata events.events
ALTER TABLE `events`.`events` ADD COLUMN country LowCardinality(String) DEFAULT '' AFTER `at`
Declaring the schema
Database
Forge

Works

  • A project with a profile, a migrate Op and a drift watch per environment, and pipelines for GitHub, GitLab and Forgejo
  • The first migration written from src/schema.ts, planned, approved and applied on chant's local emulator
  • Lint, the replay check and a plan comment on every pull request
  • Sort-key and engine changes as rebuilds into a new table, swapped in by the migration
  • Backfills as steps that resume where they stopped
  • A single node, a cluster, a Replicated database or ClickHouse Cloud
  • GitHub: The writer's secrets are secrets of a GitHub environment limited to main. The plan comment and the drift issue use the job's own token.

Proven: 15 claims on ClickHouse. Claims status

First step

npx @intentius/sql-yodeler@latest create my-schema --clickhouse --database events
Your first migration

More in Start a new schema: what differs on each database and forge, the proof, and what to read next.

npm run ci renders them for GitHub Actions, GitLab CI and Forgejo Actions, from the environments and the Ops the project declares. The two workflows.

  1. lintevery pull request

    yodel lint, the replay check on a throwaway server, and yodel ci --check on the rendered pipelines.

  2. plan-<env>every pull request

    The plan as one comment per environment, made with the reader, after a write probe that fails a job that could write.

  3. yodel-applyon a push to main

    One wave per environment, in order, each behind its gate policy as main had it.

  4. yodel approvewhen a wave waits

    A person reads the plan, types the environment, and the waiting job resumes.

  5. watch-<env>on a schedule

    Drift between the declared schema and the live one, kept in one issue.

Each environment is one wave of the apply pipeline, applied after the wave before it. Its gate policy decides when it waits for a person, and an approval covers exactly the plan it was shown, by its digest.

  1. devgate: neverjcs1-sha256:6a1f…c09eappliedapplied on the push to main
  2. staginggate: on-destructivejcs1-sha256:b77d…12a4waitingnpx yodel approve staging

refusedA migration changed after staging's approval, so staging applied nothing and named both digests.

Approval has the policies, the digest and what happens when it moves.

SQL Yodeler manages the schemas of ClickHouse and Postgres databases declaratively. You declare what the schema should be in TypeScript with chant’s sql lexicon (Declaring the schema), and the yodel CLI works out the change: it either applies it directly (the declarative path) or writes versioned migrations from it and applies those (the versioned path). Both paths apply behind an approval bound to a digest of the plan. Data migrations (rebuilds, backfills, expand-and-contract column changes) are steps inside a migration, recorded in the same history as the DDL, and resume where they stopped.

Page What it covers
Your first migration from an empty directory to applied migrations on a local database: the template, the emulator, the variables, plan, approve and apply
Installing and configuring installing yodel, the project layout, chant.config.ts profiles, yodel.config.ts, the environment variables
Starting a project a new project from a starter template, yodel init --from to adopt a database that already exists, and yodel init --baseline for another environment that holds the same schema
Coming from another migration tool for a database golang-migrate, goose, Flyway or dbmate manages: the concepts mapped, adopting it with yodel init --from, the old tool’s files and table, and rollback with yodel revert
Declaring the schema the schema as TypeScript in src/: one export per object in the database’s own DDL, references with ${}, splitting it across files, generating objects with code, composites, what the build checks and the editor
The two workflows the declarative path (yodel plan, yodel apply against the declared schema) and the versioned path (yodel new, then yodel apply)
Migrations the migrations directory, the history table, the apply lock, resume, out-of-order refusal, yodel status, yodel rebase, yodel repair, yodel checkpoint, and rollback with yodel revert
Data migrations ClickHouse rebuilds, backfills, Postgres PostgresMigrationOp steps, yodel cleanup of what they keep, and manual steps
Access control Postgres row-level security, policies, roles and grants, and ClickHouse users, roles, row policies and grants: access per environment, what the schema owns and what the environment owns, the plan’s access section, adopting them
Lint the rules, yodel:allow silences, rule levels, --env, and the replay check (--replay)
Lint and plan on pull requests only ci: { apply: false }: lint, the replay check and the plan comment on every pull request with readers only, no apply pipeline, and turning apply on later
Setting up each forge GitHub, GitLab and Forgejo side by side: the pipeline files, where the readers’ and the writer’s secrets go, the tokens for the plan comment, the drift watch and resuming, cloud roles over OIDC, runners and pull requests from forks
Approval the migrations Op and its gate, the plan digest, the pull request comment, and what happens when the digest moves
Reports and the audit log yodel report: every environment’s migrations side by side, the audit log derived from the history and the gate ledger, its gaps, audit.jsonl and the HTML run view
Schema from an ORM sources in yodel.config.ts: tables an ORM defines, read from the DDL it prints, planned and applied with the rest
Drift yodel drift and the scheduled WatchOp
Webhook events notify in yodel.config.ts: signed events for an apply waiting for approval, a refusal, a failure and drift, and checking them in a receiver
Generated schema reference yodel docs: an HTML reference and a Mermaid ERD generated from the declared schema
Topology ClickHouse on a single node, a cluster, a Replicated database and ClickHouse Cloud
The commands which command runs what (yodel, chant’s CLI, the template’s just targets), yodel --help, the exit codes, and the JSON Schemas of --json output
Set up with a coding agent the prompt to hand a coding agent, the steps it follows to add SQL Yodeler to a repository, and what it leaves to people
Glossary the chant terms yodel’s commands and output use, in yodel’s terms
Claims status every scenario claim, what it says, and whether it passed plain and was caught broken on ClickHouse and on Postgres, with the pages each one proves

Two example projects, one per dialect, show each of these on a real server. They are finished projects, and their READMEs are transcripts of the runs that made them, against chant’s emulator, with the output each command printed. To follow along step by step, start with Your first migration:

  • examples/clickhouse: adoption, a migration, a sort-key rebuild, a backfill, a lint silence, an approval that stops holding, drift, the replay check.
  • examples/postgres: adoption, lint findings, a migration that fails part way and resumes, a column rename as an expand-and-contract step, a destructive change silenced, a fork resolved with yodel rebase and yodel repair, an out-of-order migration, drift, the replay check.

The pages quote those runs.

Your first migration has the same steps by hand. Or paste this into a coding agent in your repository. The agent page says what the agent does; it never applies and never approves.

Hand the setup to your coding agentOr set up by hand: Your first migration has the same steps.
Show the whole prompt
Set up SQL Yodeler in this repository.
Read https://intentius.io/sql-yodeler/llms.txt first, then
https://intentius.io/sql-yodeler/agents/ and follow it.
Open a pull request with the result.
Never run `yodel apply` against a shared environment, never run `chant approve`, `yodel approve` or `yodel override`, never edit the `chant/lifecycle` branch or `.chant/allowed_signers`, never merge; approvals and applies belong to people.

SQL Yodeler