@lunora/cli 1.0.0-alpha.4 → 1.0.0-alpha.400

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 (130) hide show
  1. package/LICENSE.md +33 -0
  2. package/README.md +3 -1
  3. package/__assets__/package-og.svg +1 -1
  4. package/dist/bin.mjs +2 -10
  5. package/dist/index.d.mts +1161 -372
  6. package/dist/index.d.ts +1161 -372
  7. package/dist/index.mjs +1 -19
  8. package/dist/packem_chunks/dispatch.mjs +1 -0
  9. package/dist/packem_chunks/handler.mjs +1 -78
  10. package/dist/packem_chunks/handler10.mjs +1 -22
  11. package/dist/packem_chunks/handler11.mjs +1 -192
  12. package/dist/packem_chunks/handler12.mjs +1 -131
  13. package/dist/packem_chunks/handler13.mjs +3 -65
  14. package/dist/packem_chunks/handler14.mjs +1 -58
  15. package/dist/packem_chunks/handler15.mjs +1 -79
  16. package/dist/packem_chunks/handler16.mjs +1 -43
  17. package/dist/packem_chunks/handler17.mjs +7 -105
  18. package/dist/packem_chunks/handler18.mjs +1 -172
  19. package/dist/packem_chunks/handler19.mjs +1 -89
  20. package/dist/packem_chunks/handler2.mjs +1 -114
  21. package/dist/packem_chunks/handler20.mjs +3 -94
  22. package/dist/packem_chunks/handler21.mjs +3 -311
  23. package/dist/packem_chunks/handler22.mjs +1 -0
  24. package/dist/packem_chunks/handler23.mjs +2 -0
  25. package/dist/packem_chunks/handler24.mjs +99 -0
  26. package/dist/packem_chunks/handler25.mjs +2 -0
  27. package/dist/packem_chunks/handler26.mjs +9 -0
  28. package/dist/packem_chunks/handler27.mjs +1 -0
  29. package/dist/packem_chunks/handler3.mjs +1 -204
  30. package/dist/packem_chunks/handler4.mjs +3 -33
  31. package/dist/packem_chunks/handler5.mjs +1 -49
  32. package/dist/packem_chunks/handler6.mjs +1 -91
  33. package/dist/packem_chunks/handler7.mjs +1 -42
  34. package/dist/packem_chunks/handler8.mjs +2 -174
  35. package/dist/packem_chunks/handler9.mjs +1 -16
  36. package/dist/packem_chunks/runCodegenCommand.mjs +4 -52
  37. package/dist/packem_chunks/runDeployCommand.mjs +11 -504
  38. package/dist/packem_chunks/runDevCommand.mjs +2 -0
  39. package/dist/packem_chunks/runInitCommand.mjs +1176 -576
  40. package/dist/packem_chunks/runResetCommand.mjs +1 -41
  41. package/dist/packem_chunks/runRpcCommand.mjs +1 -68
  42. package/dist/packem_shared/COMMANDS-aSTSMwZS.mjs +1 -0
  43. package/dist/packem_shared/DEFAULT_IMPORT_BATCH_SIZE-CyIsI3S1.mjs +1 -0
  44. package/dist/packem_shared/EXIT_CODE-D_XCixv8.mjs +1 -0
  45. package/dist/packem_shared/admin-token-VdUnvnKW.mjs +1 -0
  46. package/dist/packem_shared/admin-url-DJB8P6Jh.mjs +1 -0
  47. package/dist/packem_shared/api-spec-lYyq4vL6.mjs +1 -0
  48. package/dist/packem_shared/binding-manifest-file-CQ1eQYy1.mjs +2 -0
  49. package/dist/packem_shared/buildRegistryIndex-Dc8D8AR6.mjs +1 -0
  50. package/dist/packem_shared/cap-error-body-YBKO32BF.mjs +1 -0
  51. package/dist/packem_shared/catalog-CahzmDLV.mjs +1 -0
  52. package/dist/packem_shared/cli-CFsmCYWp.mjs +2 -0
  53. package/dist/packem_shared/cli-manifest-DoQ_3bzs.mjs +1 -0
  54. package/dist/packem_shared/cloudflare-credentials-BVmZnDZk.mjs +1 -0
  55. package/dist/packem_shared/codegen-error-AmH54ofi.mjs +3 -0
  56. package/dist/packem_shared/command-BgjsvUFg.mjs +1 -0
  57. package/dist/packem_shared/commands-DvSfLvyt.mjs +13 -0
  58. package/dist/packem_shared/createLogger-4TujevyB.mjs +2 -0
  59. package/dist/packem_shared/createRecordingSpawner-Ch7zuQQE.mjs +1 -0
  60. package/dist/packem_shared/deploy-target-resolve-BTCW1Iv4.mjs +1 -0
  61. package/dist/packem_shared/diffSnapshots-htqNZT8Q.mjs +7 -0
  62. package/dist/packem_shared/docker-AqLAkOyY.mjs +1 -0
  63. package/dist/packem_shared/health-probe-Bw9JFfIy.mjs +1 -0
  64. package/dist/packem_shared/import-BRM1hs7j.mjs +12 -0
  65. package/dist/packem_shared/index-oHZBGBwB.mjs +1 -0
  66. package/dist/packem_shared/insertSchemaExtension-DuiV6cba.mjs +8 -0
  67. package/dist/packem_shared/lint-ignore-report-DKZagpqk.mjs +2 -0
  68. package/dist/packem_shared/mcp-config-file-CcDTeXIq.mjs +2 -0
  69. package/dist/packem_shared/open-url-EnKy--w-.mjs +1 -0
  70. package/dist/packem_shared/output-format-DWe_tkTR.mjs +2 -0
  71. package/dist/packem_shared/parseManifest-CwPTKdtS.mjs +1 -0
  72. package/dist/packem_shared/path-containment-CgxYZggb.mjs +1 -0
  73. package/dist/packem_shared/plan-DVfi3TrN.mjs +5 -0
  74. package/dist/packem_shared/planDevCommand-IglbXy5A.mjs +1 -0
  75. package/dist/packem_shared/platform-diagnostics-Cn2g6Jh-.mjs +4 -0
  76. package/dist/packem_shared/prompt-cancelled-BvsNxg_Q.mjs +1 -0
  77. package/dist/packem_shared/render-lunora-error-C3OJY5wk.mjs +2 -0
  78. package/dist/packem_shared/resolve-Bdc9KEUv.mjs +5 -0
  79. package/dist/packem_shared/resolve-target-C_ZloTvd.mjs +1 -0
  80. package/dist/packem_shared/runAddCommand-BChdJQ5L.mjs +1 -0
  81. package/dist/packem_shared/runExportCommand-slAq5BCF.mjs +5 -0
  82. package/dist/packem_shared/runMigrateGenerateCommand-BwemPIOz.mjs +11 -0
  83. package/dist/packem_shared/schemaIrToSnapshot-BHuNOfgs.mjs +1 -0
  84. package/dist/packem_shared/secret-key-8DPOH98z.mjs +1 -0
  85. package/dist/packem_shared/shared-D-zCOmgY.mjs +1 -0
  86. package/dist/packem_shared/storage-DNzYhXR-.mjs +1 -0
  87. package/dist/packem_shared/tui-prompts-B3YwUhGw.mjs +4 -0
  88. package/dist/packem_shared/variables-CPYyJtsL.mjs +1 -0
  89. package/dist/packem_shared/wrangler-name-Dsk5K1f-.mjs +1 -0
  90. package/dist/packem_shared/wrangler-secrets-CTQPpalP.mjs +1 -0
  91. package/package.json +41 -19
  92. package/skills/README.md +35 -17
  93. package/skills/lunora/SKILL.md +154 -64
  94. package/skills/lunora-create-package/SKILL.md +97 -84
  95. package/skills/lunora-deploy/SKILL.md +175 -105
  96. package/skills/lunora-functions/SKILL.md +362 -56
  97. package/skills/lunora-migration-helper/SKILL.md +162 -137
  98. package/skills/lunora-performance-audit/SKILL.md +137 -95
  99. package/skills/lunora-quickstart/SKILL.md +137 -136
  100. package/skills/lunora-realtime/SKILL.md +140 -99
  101. package/skills/lunora-setup-auth/SKILL.md +162 -102
  102. package/skills/lunora-setup-hyperdrive/SKILL.md +130 -109
  103. package/skills/lunora-setup-hyperdrive-global/SKILL.md +93 -90
  104. package/skills/lunora-setup-mail/SKILL.md +85 -113
  105. package/skills/lunora-setup-scheduler/SKILL.md +132 -96
  106. package/skills/lunora-setup-storage/SKILL.md +189 -91
  107. package/dist/packem_chunks/planDevCommand.mjs +0 -543
  108. package/dist/packem_chunks/runMigrateGenerateCommand.mjs +0 -397
  109. package/dist/packem_shared/COMMANDS-CHw4zOZ9.mjs +0 -922
  110. package/dist/packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs +0 -244
  111. package/dist/packem_shared/admin-url-4UzT-CI4.mjs +0 -19
  112. package/dist/packem_shared/api-spec-CtA6ilu4.mjs +0 -13
  113. package/dist/packem_shared/buildRegistryIndex-BcYe607_.mjs +0 -38
  114. package/dist/packem_shared/command-BDXcJCCJ.mjs +0 -14
  115. package/dist/packem_shared/commands-SUPdjsu5.mjs +0 -787
  116. package/dist/packem_shared/createLogger-CHPNjFw2.mjs +0 -73
  117. package/dist/packem_shared/defaultSpawner-DxI3mebw.mjs +0 -43
  118. package/dist/packem_shared/diffSnapshots-RR2ZE8Ya.mjs +0 -161
  119. package/dist/packem_shared/docker-hMQ97KSQ.mjs +0 -21
  120. package/dist/packem_shared/features-ocSSpZtS.mjs +0 -24
  121. package/dist/packem_shared/insertSchemaExtension-BuzF6-t2.mjs +0 -59
  122. package/dist/packem_shared/open-url-Dfq6fAyT.mjs +0 -41
  123. package/dist/packem_shared/output-format-7gyGR3h8.mjs +0 -17
  124. package/dist/packem_shared/parseArgs-YXFuKdEk.mjs +0 -56
  125. package/dist/packem_shared/parseManifest--vZf2FY1.mjs +0 -94
  126. package/dist/packem_shared/resolve-target-qbsJ_5sF.mjs +0 -16
  127. package/dist/packem_shared/runAddCommand-Txwh1Xw1.mjs +0 -4
  128. package/dist/packem_shared/schema-drift-gate-BtBt0as0.mjs +0 -79
  129. package/dist/packem_shared/schemaIrToSnapshot-aBTo7TM5.mjs +0 -43
  130. package/dist/packem_shared/wrangler-name-cy4yhm9j.mjs +0 -12
@@ -1,150 +1,220 @@
1
1
  ---
2
2
  name: lunora-deploy
3
- description: Deploys a Lunora app to Cloudflare. Use for `lunora deploy`, wrangler.jsonc
4
- bindings (SHARD/SESSION DOs, D1, R2), provisioning databases/buckets, secrets
5
- (`wrangler secret` vs `.dev.vars`), the `lunora doctor` preflight, the
6
- schema-drift gate, and dev-vs-prod separation.
3
+ description: Deploys a Lunora app to Cloudflare Workers + Durable Objects and gets wrangler.jsonc, remote resources and secrets to line up. Covers `lunora deploy` (and `--env`, `--dry-run`, `--preview`, `--migrate`), the `lunora doctor` / `lunora verify` / `lunora prepare` preflights, Durable Object bindings (SHARD, SHARD_REGISTRY, SCHEDULER), the `DB` D1 binding and its placeholder id, R2 buckets, production secrets (`lunora env push`, `wrangler secret put`) versus `.dev.vars`, the schema-drift gate, the architecture diff, and rollback with `lunora cloudflare deployments`. Use when the user asks to "deploy", "ship to production", "set up a staging environment", wire CI deploys, or when a deploy fails on a binding, a placeholder `database_id`, an unexported Durable Object class, a missing secret, or "schema drift gate blocked deploy".
7
4
  ---
8
5
 
9
6
  # Lunora Deploy
10
7
 
11
- Ship a Lunora app to Cloudflare Workers + Durable Objects. Unlike a managed
12
- backend, deployment owns real Cloudflare resources — Durable Object bindings, a
13
- D1 database, R2 buckets, and secrets — so the work is mostly making
14
- `wrangler.jsonc` and the remote resources line up.
8
+ Deploying a Lunora app means making `wrangler.jsonc`, the remote Cloudflare
9
+ resources (D1, R2, secrets) and the worker's exports agree. `lunora deploy`
10
+ automates most of that and refuses to ship when they don't.
11
+
12
+ Local development is `lunora dev` (see `lunora-quickstart`). If the deploy is
13
+ blocked by a breaking schema change, stage it with `lunora-migration-helper`
14
+ first.
15
+
16
+ ## What `lunora deploy` does
17
+
18
+ In order, stopping at the first failure:
19
+
20
+ 1. **Codegen** — regenerates `lunora/_generated/`. ERROR-level codegen
21
+ advisories fail the deploy in CI (`--strict-advisories` forces this locally,
22
+ `--no-strict-advisories` turns it off). Nothing here runs `tsc` and wrangler
23
+ strips types, so a project with a type error deploys — gate CI on
24
+ `lunora verify`, which does type-check.
25
+ 2. **Schema-drift gate** — compares the schema with the committed baseline
26
+ `lunora/.lunora-schema.json` (details below).
27
+ 3. **Architecture diff** — prints modules and edges (calls, table reads/writes,
28
+ enqueues) added or removed since the last successful deploy. Informational
29
+ only; the manifest is recorded in `.lunora/architecture[.<env>].json` after
30
+ wrangler succeeds.
31
+ 4. **Binding reconcile** — writes the bindings the code implies into
32
+ `wrangler.jsonc`: Durable Object bindings plus their `new_sqlite_classes`
33
+ migration, the `DB` D1 binding for `.global()` tables, crons, and the
34
+ compatibility date. R2 buckets are not auto-written (the bucket name is
35
+ yours to choose); you get a warning instead.
36
+ 5. **Read-only checks** — blocks on a D1 `database_id` still set to
37
+ `<replace-with-d1-create-id>`, on a loopback URL (`localhost`, `127.x`,
38
+ `::1`) in `vars`, on a missing container Dockerfile, or on no Docker engine
39
+ when a container builds locally.
40
+ 6. **Validate `wrangler.jsonc`** — the `SHARD` binding to `ShardDO` (or
41
+ `LunoraDO` for apps that merge their DOs), a minimum `compatibility_date`,
42
+ DO classes registered in `migrations`, and the shape of every binding.
43
+ `compatibility_flags` are not checked; templates ship `nodejs_compat` for
44
+ dependencies that import `node:` builtins.
45
+ 7. **Secrets gate** — lists the secrets the app's packages need and checks
46
+ which are set on the worker. Interactive runs offer to mint and push the
47
+ generatable ones (`BETTER_AUTH_SECRET`, `LUNORA_ADMIN_TOKEN`, …). In CI a
48
+ missing required secret aborts the deploy. Skipped for `--dry-run` /
49
+ `--preview`, and when the worker doesn't exist yet.
50
+ 8. **Services, then `wrangler deploy`** — services declared in
51
+ `lunora.config` deploy first (`--skip-services` to skip), then the app.
52
+ 9. **After a successful deploy** — records the URL as the project link
53
+ (`lunora link`), optionally runs `--migrate` and `--health-check`, and only
54
+ then re-blesses the schema baseline.
55
+
56
+ `lunora prepare` runs steps 1–6 without deploying (useful in CI), but it takes
57
+ no `--env`, so it validates the top-level config only. `lunora verify` runs
58
+ validation, a codegen dry run and `tsc --noEmit` without writing files, and does
59
+ take `--env`.
60
+
61
+ Flags worth knowing:
62
+
63
+ | Flag | Effect |
64
+ | -------------------------- | ------------------------------------------------------------------------------- |
65
+ | `--env <name>` | Deploy a wrangler environment; checks read that env's (non-inherited) bindings. |
66
+ | `--dry-run` | Run every gate and bundle without publishing; `wrangler.jsonc` edits roll back. |
67
+ | `--preview` | Upload a version (`wrangler versions upload`) with a preview URL; no traffic. |
68
+ | `--health-check` | Probe `/_lunora/health/ready` on the new version; fail if it never answers. |
69
+ | `--prebuilt` | Skip codegen and the drift gate when `lunora prepare` already ran in this job. |
70
+ | `--migrate` | Run every declared data migration (`up` is idempotent) against the live worker. |
71
+ | `--allow-schema-drift` | Deploy despite blocked drift; the baseline is not advanced. |
72
+ | `--update-schema-baseline` | Accept the current schema as the new baseline. |
73
+
74
+ `--migrate` is checked before wrangler runs, so a missing requirement aborts the
75
+ whole deploy:
76
+
77
+ - `--migrate-yes` — confirms running production data migrations.
78
+ - `--migrate-url <https://…>` — required unless this checkout is linked for the
79
+ same `--env` (a previous successful deploy links it automatically).
80
+ - An admin token — `--migrate-token` or `LUNORA_ADMIN_TOKEN`.
15
81
 
16
- ## When to Use
17
-
18
- - Deploying to production (or a Cloudflare environment) for the first time.
19
- - A deploy fails on a binding, a placeholder id, or the schema-drift gate.
20
- - Provisioning D1 / R2 / secrets for a Lunora app.
21
-
22
- ## When Not to Use
23
-
24
- - Local development — that is `lunora dev` (see `lunora-quickstart`).
25
- - A schema/data change that needs migrating — do that first with
26
- `lunora-migration-helper`, then deploy.
27
-
28
- ## What `lunora deploy` Does
29
-
30
- `lunora deploy` runs a fixed pipeline:
31
-
32
- 1. **Codegen** — regenerates `lunora/_generated/` and typechecks.
33
- 2. **Validate `wrangler.jsonc`** — required `compatibility_date`, the
34
- `nodejs_compat` flag, and the `SHARD` Durable Object binding.
35
- 3. **Schema-drift gate** — blocks if the committed baseline
36
- (`lunora/.lunora-schema.json`) drifted with a breaking change and no
37
- accompanying migration. The baseline is re-blessed only after the deploy
38
- succeeds.
39
- 4. **`wrangler deploy`** — builds and pushes the worker (and any container
40
- images).
41
-
42
- Useful flags: `--env <name>` (Cloudflare environment), `--migrate` (run pending
43
- data migrations against the live worker after deploy, with `--migrate-token` /
44
- `--migrate-url`), `--allow-schema-drift` (override the gate — use sparingly), and
45
- `--update-schema-baseline` (re-bless the baseline with the current shape).
46
-
47
- ## Preflight: `lunora doctor`
48
-
49
- Run the read-only preflight before deploying. It checks:
82
+ ```bash
83
+ lunora deploy --migrate --migrate-yes --migrate-url https://app.example.com
84
+ ```
50
85
 
51
- - `wrangler.jsonc` present with the `SHARD` durable-object binding.
52
- - D1 `database_id`s are real, not placeholders (`<replace>` / empty).
53
- - `send_email` destination addresses aren't placeholders.
54
- - `.dev.vars` secret-looking keys are filled.
55
- - Declared containers are exported by the worker entry.
86
+ ## Preflight
56
87
 
57
88
  ```bash
58
- lunora doctor # FAIL → exit 1; WARN/INFO don't block
89
+ lunora doctor # read-only; exits 1 on any FAIL, WARN/INFO don't block
90
+ lunora doctor --format json # each finding carries a stable `code` for CI/agents
91
+ lunora verify --env production
59
92
  ```
60
93
 
61
- `lunora verify` and `lunora prepare` run related checks (drift gate, wrangler
62
- validation) — wire `lunora verify` into CI to catch drift before a deploy.
94
+ `lunora doctor` fails on a missing or invalid `wrangler.jsonc`, a missing
95
+ `SHARD` binding, a placeholder D1 id (`d1-placeholder-id`), and a binding whose
96
+ class the worker doesn't export (`wrangler-class-unexported`), and an Artifacts
97
+ namespace in the wrong jurisdiction. It warns on unfilled `.dev.vars` secrets,
98
+ placeholder `send_email` destinations, an account near its Durable Object class
99
+ cap, and a `cloudflare.config.ts` (`cf-config-present`).
63
100
 
64
- ## `wrangler.jsonc` — the binding contract
101
+ ## `wrangler.jsonc`
65
102
 
66
- A Lunora worker needs the ShardDO (and SessionDO when auth is wired), the
67
- SQLite-DO migration tag, and whatever D1/R2 the app uses:
103
+ Templates ship this shape. The `d1_databases` and `r2_buckets` entries are
104
+ only needed when the app uses `.global()` tables or auth (D1) and storage (R2):
68
105
 
69
106
  ```jsonc
70
107
  {
71
108
  "name": "my-app",
72
- "main": "src/index.ts",
73
- "compatibility_date": "2026-04-07",
109
+ "main": "src/server.ts",
110
+ "compatibility_date": "2026-10-01",
74
111
  "compatibility_flags": ["nodejs_compat"],
75
112
  "durable_objects": {
76
113
  "bindings": [
77
114
  { "name": "SHARD", "class_name": "ShardDO" },
78
- { "name": "SESSION", "class_name": "SessionDO" }, // only with @lunora/auth
115
+ { "name": "SHARD_REGISTRY", "class_name": "ShardRegistryDO" },
79
116
  ],
80
117
  },
81
- "migrations": [{ "tag": "v1", "new_sqlite_classes": ["ShardDO", "SessionDO"] }],
82
- "d1_databases": [{ "binding": "DB", "database_name": "my-app-global", "database_id": "<replace>" }],
83
- "r2_buckets": [{ "binding": "FILES", "bucket_name": "my-app-files" }],
118
+ "migrations": [{ "tag": "v1", "new_sqlite_classes": ["ShardDO", "ShardRegistryDO"] }],
119
+ "limits": { "cpu_ms": 30000 },
120
+ "d1_databases": [{ "binding": "DB", "database_name": "my-app", "database_id": "<replace-with-d1-create-id>" }],
121
+ "r2_buckets": [{ "binding": "UPLOADS", "bucket_name": "my-app-uploads" }],
84
122
  }
85
123
  ```
86
124
 
87
- `lunora dev` auto-reconciles most of this (and `lunora registry add` adds the
88
- bindings an item needs), but the **resources themselves** must exist and their
89
- ids must be filled in:
125
+ Reconcile adds `SCHEDULER` → `SchedulerDO` when the app schedules work, and
126
+ bindings for generated containers, workflows and agents. Every `class_name`
127
+ must be exported by the worker entry or wrangler rejects the deploy.
128
+
129
+ Lunora writes the bindings but cannot create the remote resources:
90
130
 
91
131
  ```bash
92
- wrangler d1 create my-app-global # paste the returned database_id into wrangler.jsonc
93
- wrangler r2 bucket create my-app-files
132
+ wrangler d1 create my-app # paste the returned database_id into wrangler.jsonc
133
+ wrangler r2 bucket create my-app-uploads
94
134
  ```
95
135
 
96
- The DO `class_name`s must be exported by your worker entry (the
97
- `createShardDO()` / generated container exports) — wrangler rejects a binding
98
- whose class the worker doesn't export. `lunora doctor` surfaces a missing
99
- container export proactively.
136
+ When using `--env`, remember that `vars`, `d1_databases`, `durable_objects` and
137
+ `containers` do not inherit in wrangler. Each environment needs its own entries,
138
+ including its own D1 database.
100
139
 
101
- ## Secrets: `wrangler secret`, not `.dev.vars`
140
+ ## Secrets
102
141
 
103
- `.dev.vars` is **dev only** — it is git-ignored and never deployed. Production
104
- secrets (`BETTER_AUTH_SECRET`, `RESEND_API_KEY`, provider client secrets, …) go
105
- into Cloudflare:
142
+ `.dev.vars` is for development only: it is git-ignored and `wrangler deploy`
143
+ never uploads it. Production secrets live in Cloudflare:
106
144
 
107
145
  ```bash
108
- wrangler secret put BETTER_AUTH_SECRET # prompts for the value, stored encrypted
109
- wrangler secret list
146
+ lunora env diff --env production # keys local-only vs set remotely
147
+ lunora env push --env production --yes # uploads .dev.vars values; refuses placeholders
148
+ wrangler secret put RESEND_API_KEY --env production # set a prod-only value by hand
110
149
  ```
111
150
 
112
- Set every secret your app reads (mirror the secret-looking keys in `.dev.vars`)
113
- before the first request hits production.
151
+ `lunora env push` uploads the values from `.dev.vars`. If production needs
152
+ different values than dev (it usually does for provider keys), set those with
153
+ `wrangler secret put` instead.
154
+
155
+ ## Schema-drift gate
156
+
157
+ The gate blocks when the schema changed in a breaking way since
158
+ `lunora/.lunora-schema.json` and no new `defineMigration` on the affected table
159
+ covers it. The block message lists each change and a paste-ready
160
+ `lunora migrate create … --table …` command. Commit the baseline file so CI
161
+ and every machine gate against the same shape. It is advanced only after a
162
+ successful deploy, so a failed deploy never moves it past a change that didn't
163
+ ship.
164
+
165
+ Reach for `--allow-schema-drift` / `--update-schema-baseline` only when you know
166
+ existing data is compatible. Otherwise stage the change with
167
+ `lunora-migration-helper`.
168
+
169
+ ## `.global()` table DDL
114
170
 
115
- ## Dev vs Production
171
+ The runtime provisions `.global()` tables itself on first use
172
+ (`CREATE TABLE IF NOT EXISTS` plus additive `ADD COLUMN` / indexes), so additive
173
+ changes need no step. `lunora migrate generate` writes a reviewable SQL file for
174
+ D1-backed global tables, but no command applies it, `lunora deploy` included.
175
+ Destructive or hand-written statements in it (drops, a backfilling `UPDATE`, a
176
+ rename) must be applied by hand:
116
177
 
117
- - **Development:** `lunora dev` (Vite + workerd + Studio + codegen-on-save). The
118
- schema baseline and `.dev.vars` belong to dev.
119
- - **Production:** `lunora deploy`. Separate D1 database / R2 buckets / secrets
120
- from dev. Never point a dev worker at prod resources.
178
+ ```bash
179
+ wrangler d1 execute DB --remote --file=lunora/migrations/<file>.sql
180
+ ```
181
+
182
+ See `lunora-migration-helper` for the full flow.
121
183
 
122
- For `.global()` table DDL, generate and commit SQL migrations with `lunora
123
- migrate generate` before deploying; `@lunora/d1`'s runner applies them. For data
124
- backfills, deploy first, then `lunora deploy --migrate` (or `lunora migrate up
125
- --prod`). See `lunora-migration-helper`.
184
+ ## Rollback
126
185
 
127
- ## Common Pitfalls
186
+ ```bash
187
+ lunora cloudflare deployments list
188
+ lunora cloudflare deployments rollback --yes # previous version
189
+ lunora cloudflare deployments promote <version-id> --yes # send 100% of traffic to a version
190
+ ```
128
191
 
129
- 1. **Placeholder `database_id`.** The D1 binding ships with `<replace>`; run
130
- `wrangler d1 create` and paste the id. `lunora doctor` catches this.
131
- 2. **DO class not exported.** `wrangler deploy` fails if a `class_name` isn't
132
- exported by the worker entry — export `ShardDO`/`SessionDO`/generated
133
- containers.
134
- 3. **Secrets only in `.dev.vars`.** They never reach production; use `wrangler
135
- secret put` for every prod secret.
136
- 4. **Bypassing the drift gate.** `--allow-schema-drift` ships a breaking schema
137
- with no migration — stage the change (`lunora-migration-helper`) instead.
138
- 5. **Deploying with uncommitted codegen.** Commit `lunora/_generated/` and
139
- `lunora/.lunora-schema.json` so CI and the gate see the same baseline.
192
+ Rolling back code does not roll back data migrations or D1 DDL.
193
+
194
+ ## Common pitfalls
195
+
196
+ 1. **Placeholder `database_id`.** Reconcile writes
197
+ `<replace-with-d1-create-id>`; deploy refuses it. Run `wrangler d1 create`
198
+ and paste the id.
199
+ 2. **DO class not exported.** Export `ShardDO` (and any generated container /
200
+ workflow class) from the worker entry; `lunora doctor` names the gap.
201
+ 3. **Secrets only in `.dev.vars`.** They never reach production. Push or
202
+ `wrangler secret put` them before the first request.
203
+ 4. **Dev and prod sharing resources.** Give each environment its own D1
204
+ database, R2 buckets and secrets; never point a dev worker at prod data.
205
+ 5. **Uncommitted schema baseline.** Commit `lunora/.lunora-schema.json` (and
206
+ `lunora/migrations/.snapshot.json` if you use `migrate generate`).
207
+ `lunora/_generated/` is regenerated by deploy; templates git-ignore it.
208
+ 6. **Using `cf dev` / `cf build` / `cf deploy`.** They read
209
+ `cloudflare.config.ts`, which Lunora never updates
210
+ ([#964](https://github.com/anolilab/lunora/issues/964)). Use `lunora dev` /
211
+ `lunora deploy`; `cf` resource commands (zones, DNS, KV) are fine.
140
212
 
141
213
  ## Checklist
142
214
 
143
- - [ ] `lunora doctor` passes (no FAIL).
144
- - [ ] `wrangler.jsonc` has `compatibility_date`, `nodejs_compat`, the `SHARD` DO
145
- binding, and the SQLite migration tag.
146
- - [ ] D1 / R2 resources created; real ids pasted into `wrangler.jsonc`.
147
- - [ ] DO + container `class_name`s exported by the worker entry.
148
- - [ ] Production secrets set via `wrangler secret put`.
149
- - [ ] Schema changes migrated; the drift gate is green (no `--allow-schema-drift`).
150
- - [ ] `lunora deploy` succeeded; `--migrate` run if data backfills were pending.
215
+ - [ ] `lunora doctor` has no FAIL; `lunora verify --env <name>` passes in CI.
216
+ - [ ] D1 / R2 resources created per environment; real ids in `wrangler.jsonc`.
217
+ - [ ] Every DO / container / workflow `class_name` is exported by the worker entry.
218
+ - [ ] Production secrets set (`lunora env diff --env <name>` shows nothing local-only that prod needs).
219
+ - [ ] Breaking schema changes staged with a migration; drift gate green.
220
+ - [ ] `lunora deploy --env <name> --health-check` succeeded; `--migrate --migrate-yes` run if backfills were pending.