Skip to content
onwardpg
Esc
navigateopen⌘Jpreview
One evolving migration for one rolling deployment

One evolving migration.
One safe deployment.

onwardpg plans the schema old and new code share, captures the decisions PostgreSQL cannot infer, and restacks the same migration as your feature and its base evolve.

$brew install jokull/tap/onwardpg
A traveler surveying a path through a distant PostgreSQL landscape
01 Keep the feature, its history, and its route in view.

Plan compatibility.
Capture intent as it evolves.

The SQL matters. So does remembering why it is correct when models change, another migration lands, or a branch-worn development database tells a different story.

One command holds the feature together.

onwardpg plan is not a stateless “here’s the diff, good luck.” The same command grows from a nullable column to an agent-authored type bridge beneath views and indexes, while onwardpg keeps dependency order, intent, and convergence proof intact.

H
Accepted historyReplayed bundle chain
→ W
W
Working intentExported DDL, ingested by PostgreSQL
durable
D
Development realityRead-only local catalog
→ W
E
Expand checkpointRecorded by verification
P → E
P
Production realityDrift against H; readiness against E
read only
V
VerificationIndependent disposable replays
evidence
checkout-preferences / active plan
$ onwardpg plan checkout-preferences

DURABLE  H → W
  accepted base    8 bundles replayed
  working schema   materialized from Drizzle
  plan_id          plan_a71…
  decision         rename carried forward

DEVELOPMENT  D → W
  preserved        table from another branch
  local question   rename draft-only column?

 base moved: same plan restacked
 2 scoped decisions still valid

“My models changed again.”

Revise the same PlanID.No stack of speculative fixup migrations.

“Another migration landed.”

Restack on replayed history.Carry valid decisions; invalidate changed meaning.

“My dev DB is ahead.”

Keep D → W separate.Preserve other-branch objects and local-only history.
Climb from easy to nightmare

Your deployment sits in the middle—where it belongs.

01

Expand

Add the compatible surface while old code is still live.

02

Deploy

Ship one application version that understands both shapes.

03

Drain

Wait for old instances, workers, queues, and writes to finish.

04

Contract

Remove compatibility objects and converge on the desired schema.

Understand the deployment contract
add-booking-status / plan
$ onwardpg plan add-booking-status

{"status":"needs_action","durable":{"status":"needs_input","generated_plan":{"questions":[{"choices":["assert_only","manual_sql","split_plan"]}]}}}

$ onwardpg plan add-booking-status \
  --hint '{"kind":"reconcile","object":"column","name":["app","bookings","status"],"strategy":"manual_sql"}' \
  --hint '{"kind":"manual_sql","action":"reconcile_contract_sql","object":"column","name":["app","bookings","status"]}'

{"status":"needs_action","durable":{"status":"needs_sql_edits","edit_files":["phases/contract.sql"]}}

EXPAND
  ALTER TABLE "app"."bookings" ADD COLUMN "status" text;

—— deploy application, then drain old traffic ——

CONTRACT
  -- PRODUCT-SPECIFIC SQL: Provide reviewed reconcile_contract_sql SQL for app.bookings.status
  -- Verify: SELECT NOT EXISTS (SELECT 1 FROM "app"."bookings" WHERE "status" IS NULL);
  DO $onwardpg$ BEGIN IF NOT COALESCE((SELECT NOT EXISTS (SELECT 1 FROM "app"."bookings" WHERE "status" IS NULL)), false) THEN RAISE EXCEPTION 'onwardpg contract gate failed: data:1c16b884027de910'; END IF; END $onwardpg$;
  ALTER TABLE "app"."bookings" ALTER COLUMN "status" SET NOT NULL;

SQL you can inspect.
Intent that survives a rebase.

The bundle captures every consumed hint, edited pocket, hazard, assertion, and PostgreSQL major. Still-valid intent moves forward; stale answers fail.

  • Forward-only expand and contract SQL
  • Fingerprint-bound human decisions
  • Boolean data assertions
  • Empty residual diff required
See what verification proves

Built for the uncomfortable changes.

01

Compatibility-aware

Plan the shared schema

Generate expand and contract around the one application version that must understand both shapes.

02

Development-aware

Keep one plan alive

Capture intent, revise the same PlanID, and restack it as the feature and accepted base evolve.

03

Catalog-native

Know which diff this is

Keep replayed history, materialized working DDL, local development, production drift, and verification evidence distinct.

Works after your framework, not instead of it.

Export complete PostgreSQL DDL from the tool you already use. onwardpg takes it from there.

Let the agent bring context.
Make the planner constrain it.

Agents can supply semantic decisions up front, compose product-aware backfills, and read structured plan output. onwardpg consumes only intent that matches a real decision and rejects stale or unused guesses.

01Decide early

Provide known rename, drop, rollout, or backfill intent on the first plan call.

02Read production safely

Use restricted read-only MCP queries for counts and value shapes—not copied rows or write authority.

03Verify semantics

Turn observed categories into synthetic VALUES assertions without retaining production data.

Start one plan.
Keep it alive.