Skip to content

Commit bc2e9b1

Browse files
dmealingclaude
andcommitted
docs(agent-context): correct the migrate guidance shipped to adopters
The `metaobjects-verify` skill — scaffolded into every adopter repo by `meta init` and mirrored into llms.txt — told agents to run `meta migrate baseline --dialect sqlite` on a **fresh** database. That is the exact greenfield trap 0.20.1 refused at the CLI: an offline baseline derives the "existing" snapshot from the metadata, recording the target shape as already applied, so no table is ever created, every later migrate reports `no changes`, and the failure surfaces later at the API layer as `no such table`. An agent following the shipped skill would have walked a newcomer straight into it. Corrected to the working greenfield path (`--from-db … --slug init --apply`), with `baseline --from-db` kept for its real use — adopting a database that already has its schema. The skill also claimed the no-snapshot hint points at `baseline`; it points at `--from-db … --apply`. Also fixes a second inaccuracy in the same family across the skill, its reference, and both llms files: `meta migrate --db <url> --slug <name>` was described as "diff metadata vs the live DB", but without `--from-db`/`--apply` that is the OFFLINE path and `--db` is ignored. Every example now passes `--dialect` — which selects the diff pipeline, not just the SQL flavor — and distinguishes the offline, live (`--from-db`) and replay (`apply-pending`) forms. Edited at the canonical `agent-context/` source; the sdk bundle was reproduced with `bundle-agent-context.mjs` and the four `agent-context-conformance` goldens regenerated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3msoGxjRMwx94PhKSLDuE
1 parent ee878eb commit bc2e9b1

12 files changed

Lines changed: 226 additions & 93 deletions

File tree

agent-context/skills/metaobjects-verify/SKILL.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,18 @@ What this means in practice:
147147
it. You point it at the same database your server connects to:
148148

149149
```
150-
meta migrate --db postgresql://... --slug initial # emit migration SQL
151-
meta migrate --db postgresql://... --apply # apply pending migrations
152-
meta migrate --dry-run # preview without writing
150+
meta migrate --from-db --db postgresql://... --dialect postgres --slug init --apply
151+
# first migration on a brand-new database
152+
meta migrate --dialect postgres --slug add-user-shipping # everyday: emit migration SQL
153+
meta migrate --dialect postgres --slug add-user-shipping --apply --db postgresql://...
154+
# ...and apply it
155+
meta migrate --dialect postgres --slug add-user-shipping --dry-run # preview without writing
153156
```
154157

158+
`--dialect` is always required — it selects the diff pipeline, not just the SQL
159+
flavor. Do **not** run `meta migrate baseline` on a database that does not exist
160+
yet; see `references/migration.md`.
161+
155162
- Dialects: `postgres` (default), `sqlite`, and `d1` (Cloudflare D1, TS-only).
156163
- The JVM and Python ports have **no** migration command of their own — their
157164
former migrate goals/modules were removed, and (ADR-0015 Decision 2) the JVM

agent-context/skills/metaobjects-verify/references/migration.md

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,37 +40,53 @@ active format, so callers can parse them without scraping stderr.
4040

4141
## The workflow
4242

43-
### Fresh database: baseline first
43+
### Fresh database: create the tables with `--from-db … --apply`
4444

4545
The default `meta migrate` path is **offline** — it diffs metadata against a
4646
committed schema snapshot rather than the live DB. On a fresh database there is no
47-
snapshot yet; run the `baseline` step once before the first migration generate:
47+
snapshot yet, so the first command introspects the (empty) database instead:
4848

4949
```bash
50-
meta migrate baseline --dialect sqlite # seed snapshot from metadata (no DB needed)
51-
meta migrate baseline --dialect postgres # same for Postgres
52-
meta migrate baseline --from-db --db postgresql://... --dialect postgres
53-
# alternative: seed from live DB (for existing schemas)
50+
meta migrate --from-db --db file:dev.sqlite --dialect sqlite --slug init --apply
5451
```
5552

56-
`baseline` writes a reference snapshot to `.metaobjects/migrations/` and exits
57-
without emitting any SQL. After this, `meta migrate --dialect <d> --slug <name>`
58-
operates offline against that snapshot.
53+
That diffs metadata against what is *actually* in the database (nothing), emits the
54+
full `CREATE TABLE` set, applies it, and records the resulting snapshot — so the
55+
everyday offline flow works immediately afterwards, with no extra step.
56+
57+
**Do NOT reach for `meta migrate baseline` on a database that does not exist yet.**
58+
An offline baseline derives the "existing" snapshot from your *metadata*, recording
59+
your entities' target shape as already applied: no table is ever created, every
60+
later `meta migrate` reports `no changes` (exit 0), and the failure only surfaces at
61+
the API layer as `no such table`. The CLI now refuses an offline baseline when it can
62+
prove the target `--db` is empty, and its no-snapshot hint routes to the `--from-db`
63+
command above.
5964

60-
If you run `meta migrate` before baselining, the CLI surfaces a structured
61-
next-step hint pointing to the exact `baseline` command.
65+
`baseline` is for the other case — **adopting** metadata onto a database that already
66+
has its schema (a pre-existing, non-MetaObjects setup), where you want to record
67+
current state without emitting any DDL:
68+
69+
```bash
70+
meta migrate baseline --from-db --db postgresql://... --dialect postgres
71+
```
6272

6373
### Generating a migration
6474

6575
1. **Generate a migration** by diffing metadata vs the prior state (the live DB or a
6676
committed snapshot). The engine emits paired `up.sql` + `down.sql`:
6777

6878
```bash
69-
meta migrate --db postgresql://... # emit up.sql + down.sql
70-
meta migrate --db postgresql://... --slug initial # name the migration
71-
meta migrate --dry-run # preview without writing
79+
meta migrate --dialect postgres --slug add-user-shipping # offline: diff vs the committed snapshot
80+
meta migrate --dialect postgres --slug add-user-shipping --dry-run # preview without writing
81+
meta migrate --from-db --db postgresql://... --dialect postgres --slug add-user-shipping
82+
# diff vs the LIVE database instead
7283
```
7384

85+
`--dialect` is always required and is load-bearing — it selects the diff
86+
pipeline, not just the SQL flavor. The default path is offline (`--db` is
87+
ignored without `--from-db` or `--apply`); pass `--from-db` when you want the
88+
diff taken against the live database.
89+
7490
2. **Review the SQL.** Read the emitted `up.sql` (forward) and `down.sql`
7591
(rollback) before applying. Destructive changes (drop column / drop table) are
7692
opt-in — the engine blocks them unless explicitly allowed, and routes ambiguous
@@ -80,7 +96,10 @@ next-step hint pointing to the exact `baseline` command.
8096
a ledger table:
8197

8298
```bash
83-
meta migrate --db postgresql://... --apply # run pending up.sql
99+
meta migrate --dialect postgres --slug add-user-shipping --db postgresql://... --apply
100+
# generate ...and apply it
101+
meta migrate apply-pending --db postgresql://... --dialect postgres
102+
# replay committed migrations, no diff (fresh DB / CI)
84103
meta migrate --db postgresql://... --rollback <target> # run down.sql for migrations newer than <target>
85104
meta migrate --db postgresql://... --rollback "" # roll back everything (empty target)
86105
```

docs/llms/llms-full.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,11 @@ $ meta verify --codegen # regen-and-diff vs committed output
272272
$ meta verify --templates # prompt {{field}} ↔ payload-VO drift
273273
$ meta verify --db postgres://... # live-DB schema drift (Node meta only)
274274

275-
# Diff metadata vs live DB and emit migration SQL files (Node meta only, used by every port)
276-
$ meta migrate --db file:./dev.db --slug add-subscriber
275+
# Emit migration SQL files (Node meta only, used by every port). --dialect is always
276+
# required — it selects the diff pipeline, not just the SQL flavor.
277+
$ meta migrate --from-db --db file:./dev.db --dialect sqlite --slug init --apply # first migration, brand-new DB
278+
$ meta migrate --dialect sqlite --slug add-subscriber # everyday: diff vs the committed snapshot
279+
$ meta migrate --dialect sqlite --slug add-subscriber --db file:./dev.db --apply # ...and apply it
277280

278281
# Flatten loaded metadata to a canonical JSON snapshot
279282
$ meta export --out ./snapshot.json

docs/llms/llms.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ MetaObjects deliberately does **not** ship one universal binary. Schema operatio
6969
- `meta init` — scaffold `metaobjects/`, `.metaobjects/`, and `metaobjects.config.ts`, **plus** the agent context: a slim `.metaobjects/AGENTS.md` + `CLAUDE.md` (auto-wired into the project's root `CLAUDE.md`/`AGENTS.md`) and six `metaobjects-*` Claude Code skills under `.claude/skills/` scoped to the project's stack. `meta init --refresh-docs` updates only the agent docs.
7070
- `meta gen [<entity>...]` — TS codegen from entities defined under `metaobjects/`. Supports `--dry-run` and `--watch`. Generators (`entityFile()`, `queriesFile()`, `routesFile()`, `formFile()`, `tanstackQuery()`, `tanstackGrid()`, `barrel()`) come from `@metaobjectsdev/codegen-ts/generators` (and per-framework siblings) and are wired in `metaobjects.config.ts`. Per-port codegen runs in that port's own build tool: `mvn metaobjects:generate` (Java/Kotlin), `dotnet meta gen` (C#), `metaobjects gen` (Python).
7171
- `meta verify` — drift check. `verify --codegen` (regen-and-diff vs committed output), `verify --templates` (prompt `{{field}}` ↔ payload-VO drift), `verify --db` (live-DB schema drift, Node `meta` only). Per-port codegen verify: `mvn metaobjects:verify -Dmeta.verify.mode=codegen|templates` (Java/Kotlin), `dotnet meta verify` (C#), `metaobjects verify` (Python).
72-
- `meta migrate --db <url> --slug <name>` — diff metadata vs the live DB and emit migration SQL files under `.metaobjects/migrations`. Supports SQLite (`file:`, `libsql:`), Postgres (`postgres:`, `postgresql:`), and Cloudflare D1 (TS only). `--dry-run` prints SQL to stdout. **Schema migration is owned by the Node `meta` CLI (ADR-0015) and used by every port regardless of backend language** — it is shipped as a standalone binary, so a JVM/Python/C# project needs no per-language migrate engine. There is no Maven, .NET, or Python migrate command.
72+
- `meta migrate --dialect <d> --slug <name>` — diff metadata vs the committed schema snapshot and emit migration SQL files under `.metaobjects/migrations`; add `--db <url> --apply` to run them. `--dialect` is always required and selects the diff pipeline, not just the SQL flavor. On a brand-new database the first command is `meta migrate --from-db --db <url> --dialect <d> --slug init --apply` (diff against the empty database, emit `CREATE TABLE`, apply, record the snapshot) — **not** `meta migrate baseline`, which is for adopting a database that already has its schema. `meta migrate apply-pending --db <url> --dialect <d>` replays committed migrations with no diff (fresh DB / CI). Supports SQLite (`file:`, `libsql:`), Postgres (`postgres:`, `postgresql:`), and Cloudflare D1 (TS only). `--dry-run` prints SQL to stdout. **Schema migration is owned by the Node `meta` CLI (ADR-0015) and used by every port regardless of backend language** — it is shipped as a standalone binary, so a JVM/Python/C# project needs no per-language migrate engine. There is no Maven, .NET, or Python migrate command.
7373
- `meta export [--out <file>]` — flatten loaded metadata to one canonical JSON artifact.
7474

7575
## Monorepo layout

fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-verify/SKILL.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,18 @@ What this means in practice:
147147
it. You point it at the same database your server connects to:
148148

149149
```
150-
meta migrate --db postgresql://... --slug initial # emit migration SQL
151-
meta migrate --db postgresql://... --apply # apply pending migrations
152-
meta migrate --dry-run # preview without writing
150+
meta migrate --from-db --db postgresql://... --dialect postgres --slug init --apply
151+
# first migration on a brand-new database
152+
meta migrate --dialect postgres --slug add-user-shipping # everyday: emit migration SQL
153+
meta migrate --dialect postgres --slug add-user-shipping --apply --db postgresql://...
154+
# ...and apply it
155+
meta migrate --dialect postgres --slug add-user-shipping --dry-run # preview without writing
153156
```
154157

158+
`--dialect` is always required — it selects the diff pipeline, not just the SQL
159+
flavor. Do **not** run `meta migrate baseline` on a database that does not exist
160+
yet; see `references/migration.md`.
161+
155162
- Dialects: `postgres` (default), `sqlite`, and `d1` (Cloudflare D1, TS-only).
156163
- The JVM and Python ports have **no** migration command of their own — their
157164
former migrate goals/modules were removed, and (ADR-0015 Decision 2) the JVM

fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-verify/references/migration.md

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,37 +40,53 @@ active format, so callers can parse them without scraping stderr.
4040

4141
## The workflow
4242

43-
### Fresh database: baseline first
43+
### Fresh database: create the tables with `--from-db … --apply`
4444

4545
The default `meta migrate` path is **offline** — it diffs metadata against a
4646
committed schema snapshot rather than the live DB. On a fresh database there is no
47-
snapshot yet; run the `baseline` step once before the first migration generate:
47+
snapshot yet, so the first command introspects the (empty) database instead:
4848

4949
```bash
50-
meta migrate baseline --dialect sqlite # seed snapshot from metadata (no DB needed)
51-
meta migrate baseline --dialect postgres # same for Postgres
52-
meta migrate baseline --from-db --db postgresql://... --dialect postgres
53-
# alternative: seed from live DB (for existing schemas)
50+
meta migrate --from-db --db file:dev.sqlite --dialect sqlite --slug init --apply
5451
```
5552

56-
`baseline` writes a reference snapshot to `.metaobjects/migrations/` and exits
57-
without emitting any SQL. After this, `meta migrate --dialect <d> --slug <name>`
58-
operates offline against that snapshot.
53+
That diffs metadata against what is *actually* in the database (nothing), emits the
54+
full `CREATE TABLE` set, applies it, and records the resulting snapshot — so the
55+
everyday offline flow works immediately afterwards, with no extra step.
56+
57+
**Do NOT reach for `meta migrate baseline` on a database that does not exist yet.**
58+
An offline baseline derives the "existing" snapshot from your *metadata*, recording
59+
your entities' target shape as already applied: no table is ever created, every
60+
later `meta migrate` reports `no changes` (exit 0), and the failure only surfaces at
61+
the API layer as `no such table`. The CLI now refuses an offline baseline when it can
62+
prove the target `--db` is empty, and its no-snapshot hint routes to the `--from-db`
63+
command above.
5964

60-
If you run `meta migrate` before baselining, the CLI surfaces a structured
61-
next-step hint pointing to the exact `baseline` command.
65+
`baseline` is for the other case — **adopting** metadata onto a database that already
66+
has its schema (a pre-existing, non-MetaObjects setup), where you want to record
67+
current state without emitting any DDL:
68+
69+
```bash
70+
meta migrate baseline --from-db --db postgresql://... --dialect postgres
71+
```
6272

6373
### Generating a migration
6474

6575
1. **Generate a migration** by diffing metadata vs the prior state (the live DB or a
6676
committed snapshot). The engine emits paired `up.sql` + `down.sql`:
6777

6878
```bash
69-
meta migrate --db postgresql://... # emit up.sql + down.sql
70-
meta migrate --db postgresql://... --slug initial # name the migration
71-
meta migrate --dry-run # preview without writing
79+
meta migrate --dialect postgres --slug add-user-shipping # offline: diff vs the committed snapshot
80+
meta migrate --dialect postgres --slug add-user-shipping --dry-run # preview without writing
81+
meta migrate --from-db --db postgresql://... --dialect postgres --slug add-user-shipping
82+
# diff vs the LIVE database instead
7283
```
7384

85+
`--dialect` is always required and is load-bearing — it selects the diff
86+
pipeline, not just the SQL flavor. The default path is offline (`--db` is
87+
ignored without `--from-db` or `--apply`); pass `--from-db` when you want the
88+
diff taken against the live database.
89+
7490
2. **Review the SQL.** Read the emitted `up.sql` (forward) and `down.sql`
7591
(rollback) before applying. Destructive changes (drop column / drop table) are
7692
opt-in — the engine blocks them unless explicitly allowed, and routes ambiguous
@@ -80,7 +96,10 @@ next-step hint pointing to the exact `baseline` command.
8096
a ledger table:
8197

8298
```bash
83-
meta migrate --db postgresql://... --apply # run pending up.sql
99+
meta migrate --dialect postgres --slug add-user-shipping --db postgresql://... --apply
100+
# generate ...and apply it
101+
meta migrate apply-pending --db postgresql://... --dialect postgres
102+
# replay committed migrations, no diff (fresh DB / CI)
84103
meta migrate --db postgresql://... --rollback <target> # run down.sql for migrations newer than <target>
85104
meta migrate --db postgresql://... --rollback "" # roll back everything (empty target)
86105
```

fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-verify/SKILL.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,18 @@ What this means in practice:
147147
it. You point it at the same database your server connects to:
148148

149149
```
150-
meta migrate --db postgresql://... --slug initial # emit migration SQL
151-
meta migrate --db postgresql://... --apply # apply pending migrations
152-
meta migrate --dry-run # preview without writing
150+
meta migrate --from-db --db postgresql://... --dialect postgres --slug init --apply
151+
# first migration on a brand-new database
152+
meta migrate --dialect postgres --slug add-user-shipping # everyday: emit migration SQL
153+
meta migrate --dialect postgres --slug add-user-shipping --apply --db postgresql://...
154+
# ...and apply it
155+
meta migrate --dialect postgres --slug add-user-shipping --dry-run # preview without writing
153156
```
154157

158+
`--dialect` is always required — it selects the diff pipeline, not just the SQL
159+
flavor. Do **not** run `meta migrate baseline` on a database that does not exist
160+
yet; see `references/migration.md`.
161+
155162
- Dialects: `postgres` (default), `sqlite`, and `d1` (Cloudflare D1, TS-only).
156163
- The JVM and Python ports have **no** migration command of their own — their
157164
former migrate goals/modules were removed, and (ADR-0015 Decision 2) the JVM

0 commit comments

Comments
 (0)