@abloatai/ablo 0.37.1 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,392 @@
1
+ # Branch-first development
2
+
3
+ > Understand exactly what `ablo dev` prepares, what it watches, and how each Git branch gets an isolated Ablo schema and credential.
4
+
5
+ `ablo dev` is Ablo's schema-development loop. It prepares an isolated Ablo
6
+ branch for the Git branch you are working on, gives your local application a
7
+ short-lived credential for that branch, pushes the schema, and keeps the schema
8
+ registered as you edit it.
9
+
10
+ It does **not** start your application or run database migrations.
11
+
12
+ The everyday setup is two terminals:
13
+
14
+ ```bash
15
+ # Terminal 1: your application
16
+ npm run dev
17
+
18
+ # Terminal 2: Ablo's schema loop
19
+ npx ablo dev
20
+ ```
21
+
22
+ ## The mental model
23
+
24
+ ```text
25
+ Git branch
26
+
27
+ │ npx ablo dev
28
+
29
+ Ablo branch
30
+ ├── active schema artifact
31
+ ├── isolated transaction plane
32
+ ├── immutable branch id
33
+ └── expiring sk_test_ credential
34
+
35
+
36
+ gitignored .env.local
37
+
38
+
39
+ your local application
40
+ ```
41
+
42
+ A branch name is a human handle. The credential carries the immutable Ablo
43
+ branch id, so application requests cannot switch branches by changing a slug or
44
+ request parameter.
45
+
46
+ Production is the root branch. Development branches are children; they do not
47
+ inherit production write authority.
48
+
49
+ ## Before the first run
50
+
51
+ Initialize the project and sign in:
52
+
53
+ ```bash
54
+ npx ablo init
55
+ npx ablo login
56
+ ```
57
+
58
+ Login stores one project-scoped `mk_` management credential. It has no
59
+ production/test mode and no application-data authority. It can manage projects
60
+ and branches and exchange for an expiring credential bound to one branch.
61
+
62
+ If you switch projects, log in for the selected project before running `dev`:
63
+
64
+ ```bash
65
+ npx ablo projects use orders
66
+ npx ablo login --project orders
67
+ ```
68
+
69
+ ## What happens when you run it
70
+
71
+ Given:
72
+
73
+ ```bash
74
+ git switch -c feature/order-approval
75
+ npx ablo dev
76
+ ```
77
+
78
+ Ablo performs these steps.
79
+
80
+ ### 1. Discover the branch
81
+
82
+ The first available source wins:
83
+
84
+ 1. `--branch <name>`
85
+ 2. `ABLO_BRANCH`
86
+ 3. GitHub's `GITHUB_HEAD_REF` or `GITHUB_REF_NAME`
87
+ 4. Vercel's `VERCEL_GIT_COMMIT_REF`
88
+ 5. GitLab's `CI_COMMIT_REF_NAME`
89
+ 6. the current local Git branch
90
+
91
+ The reference is normalized into a lowercase plane-safe slug:
92
+
93
+ ```text
94
+ feature/order-approval → feature-order-approval
95
+ Feature: Billing V2 → feature-billing-v2
96
+ ```
97
+
98
+ Long names are shortened with a stable hash, so the same Git branch resolves to
99
+ the same Ablo branch on every machine.
100
+
101
+ ### 2. Ensure the Ablo branch
102
+
103
+ The CLI lists branches for the active project and reuses one with the same slug.
104
+ If none exists, it creates a child of the project's production root.
105
+
106
+ Creation is idempotent: two CI jobs racing to ensure the same branch converge on
107
+ the same server record.
108
+
109
+ At creation, the child receives its own copy of the parent's active schema.
110
+ Later production schema pushes do not silently change an in-flight feature
111
+ branch.
112
+
113
+ ### 3. Exchange for a temporary credential
114
+
115
+ The stored `mk_` credential authorizes the branch-management call. The server then
116
+ returns a new `sk_test_` credential bound only to the child branch.
117
+
118
+ The default lifetime is eight hours:
119
+
120
+ ```bash
121
+ npx ablo dev --branch-ttl-hours 12
122
+ ```
123
+
124
+ Allowed values are 1–168 hours. Rerunning `ablo dev` mints a fresh credential.
125
+ This lifetime belongs to the credential, not the branch. The branch persists
126
+ until `ablo branch delete` or preview automation removes it.
127
+
128
+ Temporary branch credentials can read, write, and push schema on their own
129
+ branch. They cannot create or delete siblings, mint sibling credentials, or
130
+ obtain production authority.
131
+
132
+ ### 4. Wire the local application
133
+
134
+ The temporary credential is written to:
135
+
136
+ ```dotenv
137
+ # .env.local
138
+ ABLO_API_KEY=sk_test_...
139
+ ```
140
+
141
+ The CLI creates `.env.local` with owner-only permissions when possible and adds
142
+ it to `.gitignore` if it is not already ignored. The key is not added to Ablo's
143
+ long-lived credential store.
144
+
145
+ Most application frameworks load `.env.local` automatically. Plain Node can
146
+ load it explicitly:
147
+
148
+ ```bash
149
+ node --env-file=.env.local app.ts
150
+ ```
151
+
152
+ An `ABLO_API_KEY` exported in your shell overrides `.env.local` for child
153
+ processes. `ablo dev` warns when it detects that mismatch. Unset the exported
154
+ value before starting the application:
155
+
156
+ ```bash
157
+ unset ABLO_API_KEY
158
+ npm run dev
159
+ ```
160
+
161
+ ### 5. Load and push the schema
162
+
163
+ By default, the CLI imports:
164
+
165
+ ```text
166
+ ablo/schema.ts
167
+ ```
168
+
169
+ and reads its `schema` export. Override either:
170
+
171
+ ```bash
172
+ npx ablo dev \
173
+ --schema src/ablo-schema.ts \
174
+ --export appSchema
175
+ ```
176
+
177
+ The schema is serialized and uploaded to the selected branch. The server
178
+ compares it with that branch's active artifact and returns either:
179
+
180
+ - unchanged, with the current version;
181
+ - activated, with a new version and hash; or
182
+ - rejected, with the incompatible changes and the required next action.
183
+
184
+ `push` registers a contract. It does not execute DDL.
185
+
186
+ ### 6. Watch for edits
187
+
188
+ After the first successful push, `ablo dev` watches the schema module. Editor
189
+ write/rename bursts are debounced into one reload and one push.
190
+
191
+ Stop it with `Ctrl-C`.
192
+
193
+ For a single branch preparation and push:
194
+
195
+ ```bash
196
+ npx ablo dev --no-watch
197
+ ```
198
+
199
+ ## Command reference
200
+
201
+ ```bash
202
+ # Discover from Git and watch
203
+ npx ablo dev
204
+
205
+ # Choose the branch explicitly
206
+ npx ablo dev --branch preview-pr-482
207
+
208
+ # Push once
209
+ npx ablo dev --no-watch
210
+
211
+ # Change the temporary-key lifetime
212
+ npx ablo dev --branch-ttl-hours 24
213
+
214
+ # Use another schema module/export
215
+ npx ablo dev --schema db/ablo.ts --export schema
216
+
217
+ ```
218
+
219
+ Lower-level branch operations are also available:
220
+
221
+ ```bash
222
+ npx ablo branch list
223
+ npx ablo branch status feature-orders
224
+ npx ablo branch check feature-orders
225
+ npx ablo branch create feature-orders
226
+ npx ablo branch ensure preview-pr-482 --credential --ttl-hours 168 --json
227
+ npx ablo branch credential br_... --ttl-hours 8
228
+ npx ablo branch delete br_...
229
+ ```
230
+
231
+ Automation should retain immutable ids returned by the API. Slugs are for
232
+ people and discovery.
233
+
234
+ `branch status` and `branch check` are aliases. They show lifecycle state, the
235
+ active schema and hash, compatibility with the parent schema, the bound
236
+ database's safe coordinates, and an exact readiness fix.
237
+
238
+ ## CI and preview deployments
239
+
240
+ For a one-shot CI schema check:
241
+
242
+ ```bash
243
+ # Store the project management credential as the masked secret.
244
+ ABLO_MANAGEMENT_KEY="mk_..." \
245
+ ABLO_BRANCH="preview-pr-${PR_NUMBER}" \
246
+ npx ablo dev --no-watch
247
+ ```
248
+
249
+ For infrastructure that needs the credential response directly:
250
+
251
+ ```bash
252
+ npx ablo branch ensure "preview-pr-${PR_NUMBER}" \
253
+ --kind preview \
254
+ --credential \
255
+ --ttl-hours 168 \
256
+ --json
257
+ ```
258
+
259
+ `--credential` explicitly requests plaintext secret material. Treat the JSON
260
+ result as a secret, mask it in logs, and pass it through the deployment
261
+ provider's secret-variable mechanism.
262
+
263
+ Closing a preview should call `branch delete`. Deletion immediately makes
264
+ branch-bound credentials fail authentication even if their expiry is later.
265
+
266
+ ## Managing branches in Sync Web
267
+
268
+ The persistent dashboard header selects a project and then one branch inside it. The
269
+ selection scopes API keys, Schema, Audit log, and Server log. Production is the
270
+ protected root; every other item is a named development branch.
271
+
272
+ Use the **Branches** page to create, select, inspect, or delete a branch.
273
+ Deleting a child revokes all of its credentials first, removes its datasource
274
+ connection material, and then removes it from discovery. It never deletes a
275
+ sibling, the production root, customer tables, or retained schema history.
276
+
277
+ See [Ablo branch lifecycle](../../../docs/explainers/branch-lifecycle.md) for
278
+ the complete project/branch mental model and dashboard journey.
279
+
280
+ ## Your database and migrations
281
+
282
+ There are two independent contracts:
283
+
284
+ ```text
285
+ Your ORM/migration tool owns tables, columns, relations, and DDL
286
+ Ablo schema owns the coordination contract for synced models
287
+ ```
288
+
289
+ `ablo dev` currently isolates the Ablo branch and schema. It does not:
290
+
291
+ - run Prisma, Drizzle, or SQL migrations;
292
+ - create a Neon/Supabase/RDS database branch;
293
+ - copy production data;
294
+ - automatically register whichever `DATABASE_URL` happens to be present.
295
+
296
+ The last point is a safety boundary. A generic `DATABASE_URL` does not prove
297
+ that the database is an isolated feature branch; automatically registering it
298
+ could bind a development credential to production.
299
+
300
+ Until provider-verified database-branch binding lands, use Ablo's hosted branch
301
+ plane for the inner loop or explicitly prepare and review an isolated database
302
+ through [Connect Your Database](./data-sources.md). Never point a child branch at
303
+ production merely because its migrations are compatible.
304
+
305
+ ## Production rollout
306
+
307
+ There is intentionally no `branch merge` that promotes child rows or logs.
308
+
309
+ Production rollout remains:
310
+
311
+ 1. Merge code and migration files through Git.
312
+ 2. Run the reviewed migration against the production database.
313
+ 3. Push the production/root Ablo schema through the deployment workflow.
314
+ 4. Deploy the application.
315
+
316
+ The feature branch proves the change. It does not become production.
317
+
318
+ ## Troubleshooting
319
+
320
+ ### “Creating a development branch needs the active project CLI key”
321
+
322
+ Run:
323
+
324
+ ```bash
325
+ npx ablo login
326
+ ```
327
+
328
+ For a non-default project:
329
+
330
+ ```bash
331
+ npx ablo login --project <project>
332
+ ```
333
+
334
+ If `ABLO_API_KEY` is exported, it overrides the stored login key. Unset it when
335
+ you want the CLI to use the active project's stored profile.
336
+
337
+ ### “Branch credentials cannot manage branches”
338
+
339
+ You supplied a temporary child credential to a management command. Branch
340
+ runtime credentials are deliberately leaf authority. Unset the override and let
341
+ the CLI use the project login key.
342
+
343
+ ### Check the whole branch
344
+
345
+ ```bash
346
+ npx ablo branch check feature-order-approval
347
+ ```
348
+
349
+ The result distinguishes a missing schema, a branch lifecycle failure, and an
350
+ unready connected database. A hosted branch is a valid development state and
351
+ is reported explicitly rather than as a missing database.
352
+
353
+ ### The app still reaches another plane
354
+
355
+ Check for an exported variable:
356
+
357
+ ```bash
358
+ env | grep '^ABLO_API_KEY='
359
+ ```
360
+
361
+ An exported value wins over `.env.local`. Unset it and restart the application
362
+ process so the framework reloads `.env.local`.
363
+
364
+ ### `server_execute_unknown_model`
365
+
366
+ The active branch does not have the schema containing that model. Keep
367
+ `ablo dev` running, or push once:
368
+
369
+ ```bash
370
+ npx ablo dev --no-watch
371
+ ```
372
+
373
+ Confirm that `--schema` and `--export` point at the module your application
374
+ uses.
375
+
376
+ ### Git branch discovery fails
377
+
378
+ Detached checkouts may not expose a local branch. Pass the intended name:
379
+
380
+ ```bash
381
+ npx ablo dev --branch preview-pr-482
382
+ ```
383
+
384
+ or set `ABLO_BRANCH` in CI.
385
+
386
+ ## Related guides
387
+
388
+ - [Quickstart](./quickstart.md) — install, connect, define a schema, and write.
389
+ - [CLI](./cli.md) — the complete command surface.
390
+ - [Schema Contract](./schema-contract.md) — what the registered schema controls.
391
+ - [Connect Your Database](./data-sources.md) — Postgres roles, WAL, and database ownership.
392
+ - [Deployment](./deployment.md) — production schema rollout.
package/docs/cli.md CHANGED
@@ -11,7 +11,7 @@ SQL — so what you test is what ships.
11
11
  ```bash
12
12
  npx ablo init # scaffold ablo/schema.ts + client
13
13
  npx ablo login # authorize in the browser
14
- npx ablo dev # push schema to the test sandbox + watch
14
+ npx ablo dev # prepare an isolated Git branch + push/watch
15
15
  ```
16
16
 
17
17
  **Two setup styles, and they pick your commands.** If your app database is the
@@ -19,42 +19,43 @@ source of truth, expose a [Data Source endpoint](./data-sources.md) and keep DB
19
19
  credentials in your app. If you explicitly want Ablo to open a Postgres
20
20
  connection, use the **Direct Postgres connector** commands: `ablo migrate`
21
21
  applies changes to your own `DATABASE_URL`, and `ablo check` / `ablo pull`
22
- adopt tables you already have. Hosted sandbox commands are tagged **Hosted**;
22
+ adopt tables you already have. Hosted branch commands are tagged **Hosted**;
23
23
  direct-connector commands are tagged **Direct Postgres**.
24
24
 
25
25
  ## Authenticate
26
26
 
27
27
  `ablo login` runs the OAuth 2.0 device flow: it opens your browser, you choose
28
28
  **log in** or **create an account** and approve, and the CLI provisions a
29
- **test + live key pair** (90-day) and stores them locally. The test key is a
30
- sandbox `sk_test_` key; the live key is a restricted `rk_live_` key (read-only
31
- observation `logs`, `status`), so a stolen config can't write to production.
32
- This mirrors `stripe login`.
29
+ 90-day, project-scoped `mk_` management credential. It has no test/live mode
30
+ and cannot read or write application data. `ablo dev` uses it to create or
31
+ resume a branch and exchanges it for a temporary branch-bound runtime key.
33
32
 
34
33
  | Command | What it does |
35
34
  | ------------------------ | -------------------------------------------------------------------------- |
36
- | `ablo login` | Authorize in the browser; provisions + stores a test and a live key. |
37
- | `ablo login --project <slug>` | Same, but scope (and mint) the pair to a project, and make it active. |
38
- | `ablo logout` | Remove the stored keys. |
39
- | `ablo status` | Show the active org, mode, both keys (prefix, what each can do, expiry), and server health. |
40
- | `ablo mode [sandbox\|production]` | Switch the active environment. With no argument, prompts. |
41
-
42
- Keys live in `~/.config/ablo/credentials.json` (mode `0600`), keyed by project
43
- then environment; the non-secret `config.json` holds only the active mode and
44
- project. In **CI**, don't log in — set `ABLO_API_KEY`, which always overrides
45
- the stored key.
46
-
47
- ## Test vs live
48
-
49
- Like Stripe, every account has a **test** mode and a **live** mode, and a key
50
- belongs to one of them. Test keys are bound to an isolated sandbox: their reads
51
- and writes never touch production data. Switch with `ablo mode`; `ablo dev` is always
52
- the sandbox by design.
53
-
54
- The schema is **one definition serving both**: a sandbox reads the production
55
- schema until it is pushed one of its own, so your test and live keys see the same
56
- models and only the rows differ. Each plane keeps its own copy once pushed, so a
57
- schema change reaches production when you push it with a live key — see
35
+ | `ablo login` | Authorize in the browser; store one project management credential. |
36
+ | `ablo login --project <slug>` | Same, scoped to a project, which becomes active. |
37
+ | `ablo logout` | Remove the stored credentials. |
38
+ | `ablo status` | Show the active org/project, effective credential, branch target, and server health. |
39
+
40
+ Keys live in `~/.config/ablo/credentials.json` (mode `0600`), keyed by project.
41
+ The non-secret `config.json` holds the active project. In **CI**, don't log in —
42
+ set the project management credential as `ABLO_MANAGEMENT_KEY`; it overrides the
43
+ stored credential during branch bootstrap.
44
+
45
+ ## Development branches vs live
46
+
47
+ A branch is your project at full strength over its own rows: the same models,
48
+ the same schema artifacts, the same claims and the same rules production runs.
49
+
50
+ Production is the project root. `ablo dev` creates or reuses a child branch for
51
+ your Git branch, then mints a temporary `sk_test_` key bound to that child.
52
+ Reads, writes, schema artifacts, claims, and credentials stay isolated from
53
+ production and from other development branches, which is what makes a
54
+ schema-changing pull request as routine as a code-only one.
55
+
56
+ There is no local mode switch. Development selection comes from Git or
57
+ `--branch`; production authority comes only from an explicit live credential.
58
+ Production schema changes use the reviewed one-shot path in
58
59
  [Deployment](./deployment.md).
59
60
 
60
61
  ## Projects
@@ -70,7 +71,7 @@ selects which profile every command authenticates with.
70
71
  | `ablo projects list` | List the org's projects (marks the active one and the org-default). |
71
72
  | `ablo projects create <slug>` | Create a project (`--name "Display Name"`). Its keys/schema/data are isolated. |
72
73
  | `ablo projects use <slug>` | Switch the active project. `ablo projects use default` returns to the org-default. |
73
- | `ablo login --project <slug>` | Mint and store a key pair for a project, and make it active. |
74
+ | `ablo login --project <slug>` | Store management access for a project and make it active. |
74
75
 
75
76
  Because keys are fixed to a project, `projects use` only changes which profile
76
77
  is active — it never re-scopes an existing key. Switch to a project you haven't
@@ -81,14 +82,15 @@ npx ablo projects use war-room
81
82
  # ✓ now targeting project war-room (prj_…)
82
83
  # No key stored for this project yet — run `ablo login --project war-room` to mint one.
83
84
 
84
- npx ablo login --project war-room # mints + stores its key pair, keeps it active
85
+ npx ablo login --project war-room # stores its management credential, keeps it active
85
86
  ```
86
87
 
87
88
  If you run a project-scoped command (`push`, `dev`) while the active project has
88
89
  no key — but other projects do — the CLI **refuses** rather than silently
89
90
  deploying with the wrong project's credential, and names the fix
90
- (`ablo login --project <slug>`). In CI, an explicit `ABLO_API_KEY` bypasses
91
- profiles entirely: it acts in whatever project it was minted for.
91
+ (`ablo login --project <slug>`). In CI, an explicit `ABLO_MANAGEMENT_KEY`
92
+ bypasses profiles for project/branch administration; the runtime key remains
93
+ `ABLO_API_KEY`.
92
94
 
93
95
  ## Commands
94
96
 
@@ -96,10 +98,10 @@ profiles entirely: it acts in whatever project it was minted for.
96
98
  | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
97
99
  | `ablo init` | Scaffold `ablo/` (`schema.ts`, client, optional Data Source / agent / component), write `.env`, install the SDK. Offers to log in at the end. |: |
98
100
  | `ablo login` / `logout` / `status` | Authentication & status (above). |: |
99
- | `ablo mode [sandbox\|production]` | Switch active environment. |: |
100
101
  | `ablo projects list\|create\|use\|rename` | Manage projects and the active one (see [Projects](#projects)). Each project's keys/schema/data are isolated. | `--name "<display>"` (create/rename) |
101
- | `ablo dev` | **Hosted**: push the schema to your test sandbox, then watch `ablo/schema.ts` and re-push on save. | `--no-watch`, `--schema <path>`, `--export <name>`, `--url <url>` |
102
- | `ablo logs` | Tail your scope's commit activity (`stripe logs tail`). Follows by default. | `-n, --tail <N>`, `--since <dur\|ts>`, `--model`, `--op`, `--json`, `--no-follow`, `--mode sandbox\|production` |
102
+ | `ablo dev` | **Hosted**: ensure an isolated Git branch, wire its temporary key, push, then watch `ablo/schema.ts`. | `--branch <slug>`, `--branch-ttl-hours <1-168>`, `--no-watch`, `--schema`, `--export`, `--url` |
103
+ | `ablo branch list\|status\|check\|create\|ensure\|credential\|delete` | Manage and diagnose immutable branch planes and expiring credentials. | Run `ablo branch --help`; use `--json` for automation. |
104
+ | `ablo logs` | Tail the effective credential's branch activity. Follows by default. | `-n, --tail <N>`, `--since <dur\|ts>`, `--model`, `--op`, `--json`, `--no-follow` |
103
105
  | `ablo push` | **Hosted**: upload the schema to Ablo; the server diffs, migrates, and activates it. | `--force`, `--rename old:new`, `--backfill model.field=value`, `--schema`, `--export`, `--url` |
104
106
  | `ablo migrate` | **Direct Postgres**: provision just the synced models (plus the adapter's `ablo_outbox` / `ablo_idempotency`) in your own `DATABASE_URL`. Leaves your other tables alone. | `--dry-run`, `--output <file>`, `--schema`, `--export` |
105
107
  | `ablo pull` | **Direct Postgres**: generate `defineSchema(...)` from your existing tables (read-only, like `prisma db pull`). | `--out <path>`, `--app-schema <name>`, `--import <pkg>`, `--force` |
@@ -131,16 +133,22 @@ HTTP at `/api/docs/<slug>` and through the docs MCP server.
131
133
 
132
134
  ## `ablo dev`
133
135
 
134
- The development loop. It pushes `ablo/schema.ts` to your **test sandbox**,
135
- prints the env line your app needs, then watches the file and re-pushes on every
136
- save (300 ms debounce). It refuses live keys so a tight save loop can never
137
- churn production data.
136
+ The branch-first development loop. It discovers your Git/CI branch, ensures the
137
+ matching Ablo child branch, exchanges the stored `mk_` project credential for an
138
+ expiring branch-only key, writes that key to gitignored `.env.local`, pushes
139
+ `ablo/schema.ts`, and re-pushes on every save.
138
140
 
139
141
  ```bash
140
- npx ablo dev # push + watch
141
- npx ablo dev --no-watch # push once and exit
142
+ npx ablo dev # discover from Git, push + watch
143
+ npx ablo dev --branch preview-pr-482 # explicit branch
144
+ npx ablo dev --no-watch # prepare, push once, exit
145
+ npx ablo dev --branch-ttl-hours 24 # change temporary-key lifetime
142
146
  ```
143
147
 
148
+ It does not start your app, run migrations, create a database-provider branch,
149
+ or copy production rows. Read [Branch-first development](./branch-development.md)
150
+ for the exact discovery order, CI flow, database boundary, and troubleshooting.
151
+
144
152
  ## `ablo logs`
145
153
 
146
154
  Tail commit activity, like `stripe logs tail`. Scope comes from the key — a test
@@ -212,7 +220,7 @@ reshaping it. `ablo check` is read-only; it never proposes a migration.
212
220
 
213
221
  Same engine, two setups. If you use the **Direct Postgres connector**, use
214
222
  `ablo migrate` — it provisions the synced models in your own `DATABASE_URL`. If
215
- Ablo manages the sandbox/hosted store, use `ablo push` and `ablo dev` — the
223
+ Ablo manages the hosted store, use `ablo push` and `ablo dev` — the
216
224
  server applies the change and version-gates connecting clients.
217
225
 
218
226
  ```bash
@@ -244,6 +244,17 @@ lifecycle. It needs no replication setup, which is exactly why it's the fallback
244
244
  reach for it only when logical replication isn't available, and prefer `ablo
245
245
  connect` everywhere else.
246
246
 
247
+ The current Prisma, Drizzle, and Kysely adapters are PostgreSQL bindings. Their
248
+ profiles record three independent facts: the database is PostgreSQL, the binding
249
+ is Prisma/Drizzle/Kysely, and observation is either the transactional outbox or
250
+ PostgreSQL WAL. An ORM name does not imply that the same adapter supports every
251
+ database that ORM can connect to.
252
+
253
+ The outbox automatically observes writes made through Ablo. A write made
254
+ directly by other application code is visible only if that code writes the same
255
+ outbox record in its transaction. Native WAL observation sees both Ablo and
256
+ external writes.
257
+
247
258
  ## Next steps
248
259
 
249
260
  - [Quickstart](./quickstart.md) — connect and write through `ablo.<model>`.
@@ -54,25 +54,25 @@ Everything below is those three in order.
54
54
  ### Planes: what a deployment targets
55
55
 
56
56
  A **plane** is the isolation unit a credential acts on. `production` is the root
57
- plane; every sandbox sits beside it. Three things are per-plane, and knowing
58
- which three is most of what production readiness means:
57
+ branch; development and preview branches are children. Three things are
58
+ per-plane, and knowing which three is most of what production readiness means:
59
59
 
60
- - **Rows:** a sandbox write is invisible to production and to every other sandbox.
60
+ - **Rows:** a child-branch write is invisible to production and every sibling.
61
61
  - **The registered database:** one per plane, so your production database and
62
62
  your dev database are separate registrations.
63
63
  - **The active schema artifact:** the model shapes the engine actually routes on.
64
64
 
65
- A key's plane is fixed at mint and spelled in its prefix: `sk_live_` acts on
66
- production, `sk_test_` on a sandbox. There is no runtime override — the
67
- credential *is* the environment selector, which is why application code never
68
- passes one.
65
+ A key's plane is fixed at mint: `sk_live_` acts on production, while `sk_test_`
66
+ is bound to one development branch. The immutable branch id,
67
+ not a user-supplied slug, is the selector. This is why application code never
68
+ passes an environment.
69
69
 
70
- One asymmetry is worth carrying into your deploy plan. A sandbox with no schema
71
- artifact of its own reads **production's**, so a schema pushed to production
72
- reaches your sandboxes automatically. The reverse does not hold: a push from a
73
- sandbox key creates a sandbox artifact that shadows production **for that
74
- sandbox's readers only**, and production keeps running the schema it was last
75
- pushed. Production gets its models when you push to production.
70
+ A child copies its parent's active schema when it is created, then owns its
71
+ schema history. A child push never changes production. Production gets new
72
+ models only when the reviewed deployment pushes them to the root.
73
+
74
+ There is no shared development plane. New credentials are branch-bound; follow
75
+ [Branch-first development](./branch-development.md).
76
76
 
77
77
  ## 1. The database production writes to
78
78
 
@@ -141,9 +141,10 @@ minting. Two things bite specifically at deploy time.
141
141
  observe-only `rk_live_` by design, so a stolen CLI config cannot write to
142
142
  production. A production deploy needs a **secret** `sk_live_` from the dashboard,
143
143
  supplied as `ABLO_API_KEY`. You do not have to discover this from a failed
144
- deploy: `ablo login`, `ablo mode production`, and `ablo status` each name what
145
- the key in hand does, and `ablo status --json` reports it as `effectiveKey.kind`
146
- for a pipeline to check before it pushes.
144
+ deploy: `ablo login` and `ablo status` name what the key in hand does, and
145
+ `ablo status --json` reports it as `effectiveKey.kind` for a pipeline to check
146
+ before it pushes. The deploy itself uses an explicit dashboard `sk_live_` in
147
+ `ABLO_API_KEY`; there is no local mode switch.
147
148
 
148
149
  **An explicit key always wins.** The CLI resolves `ABLO_API_KEY`, then
149
150
  `.env.local`, then `.env`, then the stored login — and `ablo status` prints which
@@ -204,9 +205,9 @@ answer to where a push would land.
204
205
 
205
206
  ## Webhooks point at the deployed URL
206
207
 
207
- `npx ablo dev` forwards commits to your machine while you build, the way
208
- `stripe listen` does. A deployed endpoint is registered once, and Ablo returns
209
- the signing secret a single time:
208
+ `npx ablo dev` prepares the schema branch; it does not forward webhooks. A
209
+ deployed HTTPS endpoint is registered once, and Ablo returns the signing secret
210
+ a single time:
210
211
 
211
212
  ```bash
212
213
  ABLO_API_KEY=sk_live_… npx ablo webhooks create https://yourapp.com/api/ablo/[...all]