Solutions — releasing everything together
A Solution is the set of things that make up one product: an app, the database behind it, the buckets it writes to, the domains it answers on. Group them once, and you can release them in one movement — in the right order, with anything that would break refused before it runs.
Most platforms deploy an app. They cannot order a database migration in front of that deploy, because they do not own the database. We do, so we can.
Make one
waymaker host solution create "Acme Portal"
waymaker host solution surface add acme-portal --kind app --id <app-id>
waymaker host solution surface add acme-portal --kind database --id <database-id>
waymaker host solution surfaces acme-portal
A surface belongs to exactly one Solution. Attaching something another Solution already owns is refused, and tells you which one has it. A Solution can hold as many of a kind as you like — three buckets and two databases is fine.
See what a release would do — before it does it
waymaker host solution plan acme-portal
0. migrate database would_apply schema changes apply before any app that reads them
1. deploy app would_apply deploys after its database, so it never boots
against an un-migrated schema
This applies nothing. It is where you find out that a database is not ready, or that an app has no repository, before anything irreversible happens. If any step would refuse, the whole release refuses — nothing partial, nothing to undo.
The order is worked out from how your surfaces are connected. You do not configure it.
Release it
waymaker host solution release apply <release-id> --migrations ./migrations.json
Apply runs the plan you already looked at, not a fresh one computed at the last moment. Steps run in order and stop at the first failure; later steps are skipped rather than attempted, because a problem that stops one surface usually affects the next.
You supply your migrations. We do not keep a queue of pending SQL and we will not invent one — so a database step refuses unless you pass the SQL with it:
{ "<app-id>": [{ "name": "001_add_orders", "sql": "CREATE TABLE orders (...);" }] }
Refusing is the point. Deploying an app whose migration has not run is exactly the failure the ordering exists to prevent.
When a release stops half way
It happens. Some steps are live, some never ran, and the record says which:
waymaker host solution release status <release-id>
From there you either continue or stop, deliberately:
waymaker host solution release resume <release-id>
waymaker host solution release abandon <release-id> --reason "waiting on the schema review"
Resume does not replay. It works out the plan again against how things are now, and carries across every step that already applied so it is not run twice. A migration that ran once does not run again.
Abandon undoes nothing. Steps that applied stay applied and keep their evidence. Releases only go forwards: to change what is live, release again.
Until you do one or the other, a new plan for that Solution is refused — planning over unfinished work is how you end up running a migration twice.
Who can release
waymaker host solution member add acme-portal <user-id> --role releaser
| Role | Can |
|---|---|
viewer | See the Solution and its release history |
releaser | Also plan and release it |
owner | Also manage who else has access |
A guest can be granted releaser — that is how a contracted developer ships your work without a
seat. It is never automatic: a guest with no grant has no access at all, and revoking their guest
access stops them releasing immediately, whatever their role here says.
Every release keeps its evidence
A release records what each step actually returned — not a status we wrote ourselves. When something
goes wrong, release status tells you which surface refused and what it said, without going near a
log.