qubu 0.6.2 → 0.7.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.
Files changed (73) hide show
  1. package/dist/codegen.d.mts +1 -1
  2. package/dist/codegen.mjs +1 -1
  3. package/dist/core.d.mts +1 -1
  4. package/dist/diff.d.mts +1 -1
  5. package/dist/index-DB07P2X8.d.mts +2 -0
  6. package/dist/index.d.mts +1 -1
  7. package/dist/introspection/mysql.d.mts +1 -1
  8. package/dist/introspection/postgres.d.mts +1 -1
  9. package/dist/introspection/sqlite.d.mts +1 -1
  10. package/dist/introspection.d.mts +3 -2
  11. package/dist/introspection.mjs +1 -1
  12. package/dist/mysql.d.mts +2 -2
  13. package/dist/postgres.d.mts +2 -2
  14. package/dist/schema.d.mts +1 -1
  15. package/dist/{serialize-Du2UPZMt.d.mts → serialize-Dg-Q55YQ.d.mts} +1 -1
  16. package/dist/snapshot/mysql.d.mts +2 -2
  17. package/dist/snapshot/postgres.d.mts +2 -2
  18. package/dist/snapshot/sqlite.d.mts +2 -2
  19. package/dist/{snapshot-mIb-Zzb5.mjs → snapshot-C1x2R-iK.mjs} +25 -9
  20. package/dist/snapshot.d.mts +3 -3
  21. package/dist/sqlite.d.mts +2 -2
  22. package/dist/{types-CiMvKi5V.d.mts → types-CdcYI0lU.d.mts} +1 -1
  23. package/dist/{types-Dqr4o2I1.d.mts → types-DcIiNePM.d.mts} +7 -0
  24. package/docs/dialects-and-execution.md +54 -25
  25. package/docs/getting-started.md +9 -9
  26. package/docs/guides/better-auth.md +16 -5
  27. package/docs/guides/compose-queries.md +21 -9
  28. package/docs/guides/drizzle.md +8 -3
  29. package/docs/guides/extensions/dialects.md +1 -1
  30. package/docs/guides/extensions/overview.md +1 -1
  31. package/docs/guides/extensions/sources-and-clauses.md +7 -3
  32. package/docs/guides/extensions/typed-expressions.md +25 -13
  33. package/docs/guides/extensions/unsafe-syntax.md +10 -6
  34. package/docs/guides/json.md +52 -27
  35. package/docs/guides/mutations.md +15 -6
  36. package/docs/guides/select/conditions.md +18 -11
  37. package/docs/guides/select/grouping-and-windows.md +5 -2
  38. package/docs/guides/select/ordering-and-pagination.md +5 -3
  39. package/docs/guides/select/overview.md +6 -3
  40. package/docs/guides/sql-templates.md +11 -5
  41. package/docs/guides/valtio-sync.md +11 -5
  42. package/docs/guides/vite-plugin.md +2 -2
  43. package/docs/index.md +24 -17
  44. package/docs/migrations/adapters.md +58 -22
  45. package/docs/migrations/adopt.md +331 -0
  46. package/docs/migrations/artifacts-and-policy.md +49 -20
  47. package/docs/migrations/index.md +18 -9
  48. package/docs/migrations/operations.md +60 -70
  49. package/docs/migrations/recovery.md +34 -17
  50. package/docs/query-model/fragments.md +13 -5
  51. package/docs/query-model/result-shapes.md +2 -2
  52. package/docs/query-model/source-scope.md +5 -3
  53. package/docs/reference/introspection-support.md +26 -19
  54. package/docs/reference/mysql-snapshot.md +19 -4
  55. package/docs/reference/postgres-snapshot.md +17 -4
  56. package/docs/reference/sqlite-snapshot.md +19 -2
  57. package/docs/reference/supported-surface.md +221 -85
  58. package/docs/schema/catalog-model.md +24 -7
  59. package/docs/schema/code-generation.md +40 -21
  60. package/docs/schema/columns-and-writes.md +21 -11
  61. package/docs/schema/constraints-and-indexes.md +12 -5
  62. package/docs/schema/ddl-emission.md +16 -5
  63. package/docs/schema/diff.md +12 -4
  64. package/docs/schema/introspection.md +47 -21
  65. package/docs/schema/migration-plans.md +18 -10
  66. package/docs/schema/snapshots.md +57 -29
  67. package/docs/schema/storage-and-schema-sql.md +10 -4
  68. package/docs/schema/tables-and-names.md +1 -1
  69. package/docs/sql-semantic-types.md +11 -8
  70. package/docs/troubleshooting.md +14 -6
  71. package/package.json +1 -1
  72. package/dist/index-CaxrMD1A.d.mts +0 -1
  73. package/docs/migrations/lotta-adoption.md +0 -50
@@ -1,6 +1,6 @@
1
1
  # Compose SQL templates
2
2
 
3
- > Use trusted SQL syntax with bound runtime values while retaining the Qubu metadata carried by interpolated expressions, fragments, and queries.
3
+ > Write SQL templates that bind values safely and preserve type information from Qubu expressions.
4
4
 
5
5
  ## Bind every runtime value
6
6
 
@@ -108,10 +108,16 @@ reaches `unsafeExpression()`.
108
108
 
109
109
  ## Preserve metadata through interpolated fragments
110
110
 
111
- The tag inherits source dependencies, conservative outer-join nullability,
112
- grouping facts, aggregate and window state, subquery state, and dialect
113
- capability requirements from Qubu fragment substitutions. It does not infer
114
- those facts from unchecked template text.
111
+ Qubu fragment substitutions pass these facts to the template:
112
+
113
+ - Required sources.
114
+ - Possible nulls from outer joins.
115
+ - Grouping dependencies.
116
+ - Aggregate and window state.
117
+ - Subquery state.
118
+ - Required dialect capabilities.
119
+
120
+ Qubu does not infer these facts from template text.
115
121
 
116
122
  Use a built-in expression as the substitution when its semantics matter:
117
123
 
@@ -102,11 +102,17 @@ const handlers = applyOpsWithQubu<SyncContext>({
102
102
  export const sync = valtioSync({ schema: { todos }, handlers })
103
103
  ```
104
104
 
105
- Authorization, conflict checks, the application mutation, and
106
- `syncEvents.write()` run in that order inside one Qubu transaction. If any step
107
- fails, the adapter rolls the transaction back. The event sequence becomes
108
- `serverVersion` unless the mutation handler returns an explicit version. Read
109
- handlers pass through unchanged.
105
+ Each mutation runs these steps in one Qubu transaction:
106
+
107
+ 1. Check authorization.
108
+ 2. Check conflicts.
109
+ 3. Apply the application mutation.
110
+ 4. Write the sync event with `syncEvents.write()`.
111
+
112
+ If any step fails, the adapter rolls the transaction back.
113
+
114
+ The event sequence becomes `serverVersion` unless the mutation handler returns
115
+ an explicit version. Read handlers pass through unchanged.
110
116
 
111
117
  The integration does not define persistence tables, import Drizzle, or execute
112
118
  driver APIs. The application owns table design, authorization, conflict policy,
@@ -1,6 +1,6 @@
1
1
  # Vite compiler hint
2
2
 
3
- > Opt a JavaScript or TypeScript module into Qubu's ambient query API while keeping the transform limited to explicit directive-bearing files.
3
+ > Use a "use qubu" directive to add the Qubu imports a module needs.
4
4
 
5
5
  The optional Vite plugin recognizes the `"use qubu"` directive and injects only
6
6
  the referenced named imports from the configured module.
@@ -33,7 +33,7 @@ ambient value and type declarations for the TypeScript compiler.
33
33
 
34
34
  ## Mark a module explicitly
35
35
 
36
- Put the directive in the module's initial directive prologue:
36
+ Put the directive at the start of the module, alongside any other directives:
37
37
 
38
38
  ```ts
39
39
  "use qubu"
package/docs/index.md CHANGED
@@ -1,11 +1,17 @@
1
1
  # Qubu
2
2
 
3
- > Build parameterized SQL from typed tables, expressions, and clauses.
3
+ > Build SQL queries from typed tables and reusable values.
4
4
 
5
- Qubu builds SQL from values. Tables, expressions, clauses, and complete queries
6
- compose without a mutable query builder. TypeScript tracks selected row shapes,
7
- source scope, and nullability, while rendering returns SQL text and ordered
8
- parameters.
5
+ Qubu builds SQL from reusable values. You can combine query parts without
6
+ changing a shared query-builder object.
7
+
8
+ TypeScript checks:
9
+
10
+ - Which fields the query returns and their types.
11
+ - Whether each column belongs to a source in the query.
12
+ - Whether a result can be `null`.
13
+
14
+ Render a query to inspect its SQL text and ordered parameters.
9
15
 
10
16
  The preferred source style names each projected field and writes the final
11
17
  `select()` clauses in SQL order. Clause values remain order-independent at
@@ -17,7 +23,7 @@ and placed in that final call where it reads best.
17
23
  If this is your first query, follow [Getting started](getting-started.md) to
18
24
  define a table, build a `SELECT`, and inspect its SQL and parameters.
19
25
 
20
- ## Choose a task
26
+ ## Build and run queries
21
27
 
22
28
  - [Build a `SELECT`](guides/select/overview.md) with projections, joins,
23
29
  predicates, ordering, and grouping.
@@ -36,18 +42,18 @@ define a table, build a `SELECT`, and inspect its SQL and parameters.
36
42
  - [Query nested JSON](guides/json.md) or read scalars from structured paths.
37
43
  - [Enable the Vite compiler hint](guides/vite-plugin.md) when query modules
38
44
  should opt into named imports through a directive.
39
- - [Inspect an existing database](schema/introspection.md) through the optional
40
- user-owned catalog boundary.
41
- - [Generate a schema module](schema/code-generation.md) from one complete,
42
- non-lossy Snapshot v1 introspection result.
45
+
46
+ ## Work with schemas and migrations
47
+
48
+ - [Inspect an existing database](schema/introspection.md) using a connection you provide.
49
+ - [Generate a schema module](schema/code-generation.md) from an introspection result without losing schema facts.
43
50
  - [Compare snapshots](schema/diff.md) with explicit rename hints and reviewable
44
51
  safety diagnostics.
45
52
  - [Build migration plans](schema/migration-plans.md) as reviewed, deterministic
46
53
  data before DDL emission.
47
54
  - [Emit DDL](schema/ddl-emission.md) from an approved migration plan without
48
55
  handing Qubu a database connection.
49
- - [Operate migrations](migrations/index.md) with versioned artifacts, verified
50
- adapter profiles, baselines, a portable executor, and explicit recovery.
56
+ - [Operate migrations](migrations/index.md) with reviewed migration files and a verified adapter.
51
57
 
52
58
  ## The query pipeline
53
59
 
@@ -106,8 +112,9 @@ render(query)
106
112
  The inferred row is `{ id: number; name: string }`. The value `7` stays out
107
113
  of the SQL text and appears in the `parameters` array in placeholder order.
108
114
 
109
- The [supported features](reference/supported-surface.md) page is the canonical
110
- package-entrypoint and ownership map. Applications may use Qubu's portable
111
- migration executor while retaining credentials, approval policy, custom SQL,
112
- and deployment lifecycle ownership. [Troubleshooting](troubleshooting.md)
113
- starts from common errors and points to the concept page behind each one.
115
+ ## Find support details
116
+
117
+ - [Supported features](reference/supported-surface.md) lists package imports and
118
+ explains which responsibilities stay with your application.
119
+ - [Troubleshooting](troubleshooting.md) starts from common errors and explains
120
+ how to fix them.
@@ -1,6 +1,6 @@
1
1
  # Adapter capability profiles
2
2
 
3
- > Select a migration adapter from capabilities proven by its driver and environment, not from dialect name alone.
3
+ > Choose a migration adapter based on what its driver and environment have been tested to support.
4
4
 
5
5
  Every executable migration adapter opens a migration session and
6
6
  advertises the exact behavior the executor may use:
@@ -25,12 +25,12 @@ executor never treats one as proof of the other.
25
25
 
26
26
  ## Trusted migration SQL
27
27
 
28
- Migration SQL, including SQL conditions, is trusted across all adapters. Qubu
29
- validates program structure and adapter capabilities, but does not parse SQL
30
- to enforce safety. Callers must preserve executor-owned transactions, connection
31
- settings, and migration journal state. For example, an explicit `COMMIT` can
32
- leave schema changes applied without their journal record; atomicity and recovery
33
- guarantees depend on respecting this contract.
28
+ Qubu validates program structure and adapter capabilities. It trusts the SQL
29
+ you supply, including SQL conditions, and does not parse it for safety.
30
+
31
+ Migration SQL must preserve the executor’s transactions, connection settings,
32
+ and journal state. For example, an explicit `COMMIT` can apply schema changes
33
+ without their journal record, breaking the executor’s recovery guarantees.
34
34
 
35
35
  ## Current profiles
36
36
 
@@ -44,11 +44,32 @@ The following stable profiles have live conformance coverage in this checkout:
44
44
  | `@qubu/adapter-postgresjs/migration` | PostgreSQL | required, optional, forbidden | none, exclusive | checkpointed | Reserves and releases one connection |
45
45
  | `@qubu/adapter-pglite/migration` | PostgreSQL | required, optional, forbidden | none, exclusive | checkpointed | Uses the database query queue as the pinned session |
46
46
 
47
- All five support every current tagged parameter kind (`null`, `boolean`,
48
- `string`, `number`, `bigint`, `bytes`, and `json`), a database journal and
49
- lease, atomic applied-record/head advancement, and recovery-required commit
50
- ambiguity classification. Support still depends on the artifact's server,
51
- feature, transaction, and lock constraints.
47
+ All five support:
48
+
49
+ - Every current tagged parameter kind.
50
+ - A journal and migrator lease stored in the database.
51
+ - An atomic update of the applied record and journal head.
52
+ - A recovery-required result when a commit’s outcome is uncertain.
53
+
54
+ Parameter kinds are:
55
+
56
+ - `null` and `boolean`.
57
+ - `string` and `number`.
58
+ - `bigint` and `bytes`.
59
+ - `json`.
60
+
61
+ The artifact’s server, feature, transaction, and lock requirements must still
62
+ match the adapter.
63
+
64
+ ### Unavailable profiles
65
+
66
+ MySQL has a [basic SQL migration runner](../../adapters/mysql2/README.md#run-sql-migrations),
67
+ `migrate`, which executes pending statements and records completed
68
+ migrations. It runs independently of the shared executor and its stronger
69
+ capability contract. The MySQL profile below remains unavailable; possible
70
+ improvements are tracked in [issue #1](https://github.com/aleclarson/qubu/issues/1).
71
+ The same entry point exports `baselineAdapter` for [reviewed MySQL adoption](adopt.md#mysql)
72
+ through the shared CLI and `@qubu/migrate/baseline` API.
52
73
 
53
74
  These exported profiles are unavailable and must not be passed to the
54
75
  executor:
@@ -62,16 +83,18 @@ executor:
62
83
  Unavailable profiles expose `reason` and `missingCapabilities`; they do not
63
84
  fall back to a generic executor.
64
85
 
86
+ ### Configure libSQL inspection
87
+
65
88
  For libSQL, let the migration entrypoint exclude all reserved journal objects
66
89
  during strict inspection:
67
90
 
68
91
  ```ts
69
92
  import { createClient } from "@libsql/client"
70
- import { libsqlMigrationAdapter, readLibsqlMigrationSnapshot } from "@qubu/adapter-libsql/migration"
93
+ import { migrationAdapter, readMigrationSnapshot } from "@qubu/adapter-libsql/migration"
71
94
 
72
95
  const client = createClient({ url: process.env.DATABASE_URL! })
73
- const adapter = libsqlMigrationAdapter(client, {
74
- readSnapshot: readLibsqlMigrationSnapshot,
96
+ const adapter = migrationAdapter(client, {
97
+ readSnapshot: readMigrationSnapshot,
75
98
  })
76
99
  ```
77
100
 
@@ -84,7 +107,9 @@ Each executable artifact must contain exactly one phase and an embedded before
84
107
  snapshot. The adapter submits its statements, SQL assertions, applied-history
85
108
  record, head update, and terminal attempt state in one `client.migrate()` call.
86
109
  For example, creating a table and recording that migration either both commit
87
- or both roll back. Multiple artifacts are separate batches; earlier successful
110
+ or both roll back.
111
+
112
+ Multiple artifacts are separate batches; earlier successful
88
113
  artifacts remain applied if a later one fails.
89
114
 
90
115
  Preparation reads the schema in a read transaction. The submitted batch checks
@@ -92,13 +117,24 @@ that the catalog still matches that inspection, the lease is still owned, and
92
117
  the head still equals the expected parent. Foreign-key validation runs before
93
118
  commit because libSQL temporarily disables enforcement during `migrate()`.
94
119
 
120
+ ### Supported conditions
121
+
95
122
  Schema fingerprint and property preconditions are checked against the embedded
96
- before snapshot, whose physical facts are verified during preparation and
97
- guarded by the in-batch catalog assertion. Object-presence and scalar SQL checks
98
- run inside the batch. Postconditions must be object-presence/absence checks
99
- without fingerprints, or scalar SQL checks returning `1`. Unsupported conditions
100
- and multiple phases are rejected. SQL content is passed to the driver without
101
- safety validation; each program entry must follow the driver's statement contract.
123
+ before snapshot. Preparation verifies its physical facts, and the batch
124
+ asserts that the catalog still matches.
125
+
126
+ Object-presence and scalar SQL checks run inside the batch. Postconditions
127
+ must use either:
128
+
129
+ - Object-presence or absence checks without fingerprints.
130
+ - Scalar SQL checks returning `1`.
131
+
132
+ Unsupported conditions and multiple phases are rejected.
133
+
134
+ SQL is passed to the driver without safety validation. Each program entry
135
+ must follow the driver’s statement contract.
136
+
137
+ ### Crashes and uncertain outcomes
102
138
 
103
139
  The database-row lease has no expiry or heartbeat. A process crash can leave
104
140
  it held; ownership must be resolved before another runner can proceed. A lost
@@ -0,0 +1,331 @@
1
+ # Adopt an existing database
2
+
3
+ > Capture and accept the live schema as migration history's starting point, then
4
+ > reconcile it with the application's desired schema separately.
5
+
6
+ If the live `game` table lacks `manifest`, the captured baseline must also lack
7
+ it. Adoption changes no application schema/data and does not certify that the
8
+ desired application version can run. Keep incompatible code stopped until a
9
+ separate reviewed migration reconciles the schema.
10
+
11
+ ## Choose an adapter
12
+
13
+ All supported adoption adapters use the same [CLI](#shared-cli) and
14
+ [API](#shared-api). Each checks an empty artifact repository and its own history.
15
+
16
+ | Database and adapter | History storage | Coordination |
17
+ | ----------------------- | -------------------------- | ---------------------------------------------------- |
18
+ | SQLite through `libsql` | Migration journal | Adapter lease coordinates participating Qubu runners |
19
+ | PostgreSQL through `pg` | Migration journal | Pinned client and advisory lease |
20
+ | MySQL through `mysql2` | `__qubu_mysql2_migrations` | Caller serializes runners; no database lease |
21
+
22
+ Adoption does not require migration execution capabilities. MySQL supports the
23
+ shared adoption commands while retaining its basic SQL runner for subsequent
24
+ migrations. See [adapter profiles](adapters.md) for execution contracts.
25
+
26
+ Configure the desired application Snapshot v1 as the managed `scope`
27
+ (`config.snapshot` for the CLI). Preserve the same database, namespace, adapter,
28
+ and original scope through acceptance, including tables that are currently
29
+ absent. A candidate does not persist that original selection or bind itself to
30
+ a particular database. Verify the connection independently; reported selectors
31
+ do not prove database identity. Keep credentials out of snapshots and metadata.
32
+
33
+ ### Inspection scope
34
+
35
+ Standard readers select managed tables by their expected physical names and
36
+ preserve logical identities. Expected facts never fill missing tables or columns.
37
+ Other live tables are reported as unmanaged.
38
+
39
+ | Adapter | Connection and namespace | Other inspected objects |
40
+ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
41
+ | `libsql` | Configure the existing database as in [CLI configuration](operations.md#configuration) | Non-table objects retain the adapter's inspection policy; strict inspection can reject unsupported facts outside selected tables |
42
+ | `pg` | Connected `Client` or acquired `PoolClient`; keep `search_path` equal to the scope's existing namespace; see [PostgreSQL configuration](#postgresql-configuration) | Excluded tables take their sequences, triggers, policies, comments, and ownership with them. Namespace-level views, free sequences, enums, domains, routines, extensions, and opaque evidence remain included |
43
+ | `mysql2` | Dedicated connection, autocommit enabled, no active transaction; the selected database must equal the scope's database | Excluded tables take attached triggers, partitions, and comments with them. Namespace-level views, routines, used collations, and opaque evidence remain included |
44
+
45
+ Readers exclude reserved `__qubu_migration_` objects and their attached metadata;
46
+ MySQL also excludes `__qubu_mysql2_migrations`. Strict catalog failures remain
47
+ errors. A managed reference to an excluded object can require a broader scope.
48
+
49
+ The adapters expose `readMigrationSnapshot` directly. Without an expected scope,
50
+ the `pg` reader inspects non-journal objects in `public`; the `mysql2` reader uses
51
+ the selected database. Use `captureBaseline` for adoption so journal setup
52
+ precedes inspection, including any catalog facts introduced by that setup.
53
+
54
+ ## Capture, review, and verify
55
+
56
+ 1. **Capture** the actual managed schema to a new candidate file. The candidate
57
+ is an ordinary Snapshot v1, not a migration artifact. Keep it outside the
58
+ shared artifact directory and refuse to overwrite an existing candidate.
59
+ 2. **Review** its tables, columns, defaults, constraints, indexes, comments,
60
+ dialect facts, included namespace objects, and reported unmanaged tables.
61
+ 3. **Preflight** the explicitly reviewed file against a fresh inspection of the
62
+ original scope. Require empty history and an identical canonical snapshot,
63
+ including metadata. Changed facts or a newly appeared managed table block
64
+ acceptance. Investigate, recapture, and review; do not patch expected facts
65
+ into the candidate to hide differences.
66
+ 4. **Accept** only after verifying the acknowledgments below. Acceptance repeats
67
+ preflight and records a non-executable baseline at sequence zero with a null
68
+ parent. Preserve the returned or written artifact.
69
+
70
+ Capture and preflight record no baseline and change no application schema/data.
71
+ Session setup may initialize Qubu history tables and perform lease bookkeeping.
72
+ Stop other migrators and prevent concurrent DDL throughout cutover. Leases
73
+ coordinate participating Qubu runners, not every possible schema writer.
74
+ Preflight needs no acceptance confirmations. Comparison failures include
75
+ diagnostics and actual snapshot evidence when available.
76
+
77
+ ### Acceptance acknowledgments
78
+
79
+ All seven facts must be true. The application acknowledgment means incompatible
80
+ code remains stopped; it does not assert that desired code is already compatible.
81
+
82
+ | CLI confirmation | What the operator verifies |
83
+ | ------------------------------------ | ---------------------------------------------------------------------------------- |
84
+ | `database-target` | The configured connection targets the intended database and environment |
85
+ | `snapshot-source` | The selected candidate was captured and reviewed using the unchanged managed scope |
86
+ | `zero-managed-drift` | Fresh strict inspection has zero differences from the reviewed candidate |
87
+ | `backup-restore-ready` | Backup and restore procedures are ready |
88
+ | `other-migrators-stopped` | Other runners are stopped and concurrent DDL is prevented |
89
+ | `incompatible-application-prevented` | Incompatible code will remain stopped until reconciliation is complete |
90
+ | `legacy-history-cutover` | The team accepts this baseline as the new lineage start |
91
+
92
+ ## Shared CLI
93
+
94
+ Install `@qubu/cli`, `@qubu/migrate`, and the selected adapter and driver.
95
+ Configure the [CLI](operations.md#configuration) with the existing database,
96
+ desired application snapshot, and an empty artifact directory. Standard `libsql`
97
+ and `pg` migration adapters provide strict inspection; intentional `readSnapshot`
98
+ overrides remain caller-owned. The CLI accepts either a migration adapter or an
99
+ adoption-only `baselineAdapter` in `config.adapter`. See the connection examples
100
+ for [PostgreSQL](#postgresql-configuration) and [MySQL](#mysql).
101
+
102
+ Capture and review:
103
+
104
+ ```bash
105
+ qubu migrate baseline-capture --out ./baseline-candidate.json \
106
+ --config ./qubu.config.js --format json
107
+ ```
108
+
109
+ The CLI refuses to overwrite the file and reports the config path, environment,
110
+ dialect, namespace, configured managed tables, included tables, and exclusions.
111
+ After reviewing that output and candidate, run preflight:
112
+
113
+ ```bash
114
+ qubu migrate baseline initial --candidate ./baseline-candidate.json \
115
+ --config ./qubu.config.js --dry-run --format json
116
+ ```
117
+
118
+ Once all [acknowledgments](#acceptance-acknowledgments) are verified, accept:
119
+
120
+ ```bash
121
+ qubu migrate baseline initial --candidate ./baseline-candidate.json \
122
+ --config ./qubu.config.js \
123
+ --confirm database-target \
124
+ --confirm snapshot-source \
125
+ --confirm zero-managed-drift \
126
+ --confirm backup-restore-ready \
127
+ --confirm other-migrators-stopped \
128
+ --confirm incompatible-application-prevented \
129
+ --confirm legacy-history-cutover \
130
+ --format json --non-interactive
131
+ ```
132
+
133
+ ### Shared API
134
+
135
+ Import adoption operations from `@qubu/migrate/baseline` and `baselineAdapter`
136
+ from the selected driver's `/migration` entry point. For example, with a
137
+ dedicated MySQL connection:
138
+
139
+ ```ts
140
+ import { baselineAdapter } from "@qubu/adapter-mysql2/migration"
141
+ import { captureBaseline } from "@qubu/migrate/baseline"
142
+ import { encodeSchemaSnapshot } from "qubu/snapshot"
143
+ import { writeFile } from "node:fs/promises"
144
+
145
+ const adapter = baselineAdapter(connection)
146
+ const inspection = await captureBaseline({ adapter, scope: desiredSnapshot })
147
+ await writeFile("baseline-candidate.json", encodeSchemaSnapshot(inspection.snapshot), {
148
+ flag: "wx",
149
+ })
150
+ ```
151
+
152
+ Review the candidate before continuing. Reuse the same database and original
153
+ scope, and reload the reviewed file for preflight and acceptance:
154
+
155
+ ```ts
156
+ import { readFile, writeFile } from "node:fs/promises"
157
+ import { preflightBaseline, createBaseline } from "@qubu/migrate/baseline"
158
+ import { encodeBaselineArtifact } from "@qubu/migrate/artifact"
159
+ import { assertSchemaSnapshot } from "qubu/snapshot"
160
+
161
+ const candidate = assertSchemaSnapshot(await readFile("baseline-candidate.json", "utf8"))
162
+ const input = { adapter, scope: desiredSnapshot, candidate, repository: [] }
163
+ await preflightBaseline(input)
164
+
165
+ const { artifact } = await createBaseline({
166
+ ...input,
167
+ id: "initial",
168
+ provenance: { source: "my-service" },
169
+ confirmation: {
170
+ databaseTargetVerified: true,
171
+ snapshotSourceVerified: true,
172
+ zeroManagedDriftVerified: true,
173
+ backupRestoreReady: true,
174
+ otherMigratorsStopped: true,
175
+ incompatibleApplicationPrevented: true,
176
+ legacyHistoryCutoverAccepted: true,
177
+ },
178
+ })
179
+ await writeFile("baseline-accepted.json", encodeBaselineArtifact(artifact), { flag: "wx" })
180
+ ```
181
+
182
+ Supply the real artifact repository when one exists; `[]` represents an empty
183
+ repository. The adapter separately verifies its database history. The caller
184
+ owns connection cleanup, including after failed capture or acceptance.
185
+
186
+ Existing custom migration adapters can use `fromMigrationAdapter(adapter)` from
187
+ `@qubu/migrate/baseline`. It reuses their strict reader, lease, and journal. A
188
+ custom adoption-only adapter implements `openBaselineSession`; its session
189
+ provides schema inspection, empty-history verification, baseline recording,
190
+ and cleanup. It does not need SQL execution or migration recovery methods.
191
+
192
+ ### PostgreSQL configuration
193
+
194
+ The caller owns connecting, releasing, and ending the pinned `pg` client. Keep
195
+ it exclusively available until the migration session closes. Qubu releases its
196
+ locks and closes its session; it never releases or ends the supplied client.
197
+
198
+ This CLI wrapper opens a client per session and ends it after cleanup. Set
199
+ `search_path` to the snapshot namespace: inspection selects it explicitly, while
200
+ journal SQL and unqualified migration statements use `search_path`.
201
+
202
+ ```ts
203
+ import { defineConfig } from "@qubu/cli/config"
204
+ import { migrationAdapter } from "@qubu/adapter-pg/migration"
205
+ import { Client } from "pg"
206
+ import snapshot from "./schema.snapshot.js"
207
+
208
+ export default defineConfig({
209
+ artifacts: "./migrations",
210
+ snapshot,
211
+ environment: "production",
212
+ provenance: { source: "my-service" },
213
+ adapter: () => ({
214
+ async openMigrationSession(signal) {
215
+ const connectionString = process.env.DATABASE_URL
216
+ if (!connectionString) throw new Error("DATABASE_URL is required")
217
+ const client = new Client({ connectionString })
218
+ try {
219
+ await client.connect()
220
+ const namespace = '"' + snapshot.namespace.name.replaceAll('"', '""') + '"'
221
+ await client.query("SELECT set_config('search_path', $1, false)", [namespace])
222
+ const session = await migrationAdapter(client).openMigrationSession(signal)
223
+ const close = session.close.bind(session)
224
+ let closed = false
225
+ session.close = async () => {
226
+ if (closed) return
227
+ closed = true
228
+ try {
229
+ await close()
230
+ } finally {
231
+ await client.end()
232
+ }
233
+ }
234
+ return session
235
+ } catch (error) {
236
+ await client.end()
237
+ throw error
238
+ }
239
+ },
240
+ }),
241
+ })
242
+ ```
243
+
244
+ ## MySQL
245
+
246
+ Use a dedicated `mysql2/promise` connection with autocommit enabled and no active
247
+ transaction. This configuration opens a connection per adoption session and
248
+ ends it after cleanup. Run the [shared CLI commands](#shared-cli) with this config:
249
+
250
+ ```ts
251
+ import { defineConfig } from "@qubu/cli/config"
252
+ import { baselineAdapter } from "@qubu/adapter-mysql2/migration"
253
+ import mysql from "mysql2/promise"
254
+ import snapshot from "./schema.snapshot.js"
255
+
256
+ export default defineConfig({
257
+ artifacts: "./migrations",
258
+ snapshot,
259
+ environment: "production",
260
+ provenance: { source: "my-service" },
261
+ adapter: () => ({
262
+ async openBaselineSession(scope, signal) {
263
+ const connection = await mysql.createConnection(process.env.DATABASE_URL!)
264
+ try {
265
+ const session = await baselineAdapter(connection).openBaselineSession(scope, signal)
266
+ return {
267
+ ...session,
268
+ async close() {
269
+ try {
270
+ await session.close()
271
+ } finally {
272
+ await connection.end()
273
+ }
274
+ },
275
+ }
276
+ } catch (error) {
277
+ await connection.end()
278
+ throw error
279
+ }
280
+ },
281
+ }),
282
+ })
283
+ ```
284
+
285
+ Acceptance inserts one baseline record in `__qubu_mysql2_migrations`. Its
286
+ `baseline` column contains the full sealed artifact, including the reviewed
287
+ snapshot, provenance, and acknowledgments. The CLI also writes that artifact to
288
+ the configured repository.
289
+
290
+ This adoption-only configuration cannot run `migrate apply`, `status`, or
291
+ `reconcile`, which require the shared migration executor. Use the basic MySQL
292
+ runner for subsequent SQL migrations as shown below.
293
+
294
+ ## After acceptance
295
+
296
+ If recording or artifact-file writing fails, inspect history and saved files
297
+ before retrying. The shared CLI records the journal before writing the artifact;
298
+ MySQL can lose an insert response after the record commits. Rerunning adoption
299
+ against nonempty history is not a recovery procedure. For MySQL, inspect:
300
+
301
+ ```sql
302
+ SELECT id, hash, baseline FROM __qubu_mysql2_migrations WHERE id = 'initial';
303
+ ```
304
+
305
+ Reconcile the desired application schema in a separate reviewed migration.
306
+ For SQLite/PostgreSQL, keep `config.snapshot` as the desired snapshot and plan
307
+ from the accepted baseline:
308
+
309
+ ```bash
310
+ qubu migrate create add-manifest --config ./qubu.config.js --dry-run --format json
311
+ ```
312
+
313
+ Review approvals or custom programs, create the artifact, and apply it through
314
+ the [normal workflow](operations.md). The tested path extends the captured
315
+ catalog snapshot with a supported change. Independently authored snapshots can
316
+ differ in constraint names, native types, defaults, or dialect metadata even
317
+ when their SQL seems equivalent. Adoption adds no general SQL equivalence rules
318
+ or automatic repairs.
319
+
320
+ For MySQL, review changes against the accepted snapshot and append SQL migrations
321
+ with distinct, permanent IDs:
322
+
323
+ ```ts
324
+ import { migrate } from "@qubu/adapter-mysql2/migration"
325
+
326
+ await migrate(connection, [{ id: "add-manifest", sql: ["ALTER TABLE game ADD manifest JSON"] }])
327
+ ```
328
+
329
+ The basic runner skips completed IDs. It does not check live schema drift before
330
+ subsequent migrations or consume sealed executable artifacts. Partial failures
331
+ still require inspection before retrying.