Repository navigation
Commit c0fb794
fix(cli,runtime): the ownership refusal names the database for os dev -d, and the driver and token given by flag (#31581)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1X2kM3DQTy5XByDHKN47ZrVA)_
domain:server#5 · os-project-manager · session_01FEkQ5mbAyTfp7gbjowRA48
Fixes #31547
Clause-②: yes (widening) — `@objectstack/runtime`'s published
`OrganizationOwnershipBootGuardOptions` gains two optional members,
`databaseDriver` and `databaseAuthTokenFromFlag`, and the refusal's two
ceremony commands set `OS_DATABASE_DRIVER` / `OS_DATABASE_AUTH_TOKEN` in
front when `--database-driver` / `--database-auth-token` chose them; `os
dev`'s `--dev` commands carry `--database-url` when `-d` chose the
database. What `os dev`, `os start`, `os serve` and `os migrate
organization-ownership` accept or refuse is unchanged.
Metadata-protocol: none
## 维护者速读
- **改了什么**: `os dev -d URL` 因组织归属被拒时,`--dev` 两条仪式命令现在带 `--database-url
<被拒的库>`,经 #31546 的同一内部通道与守卫选项传递;`os start` / `os dev` 用
`--database-driver`、`--database-auth-token` 启动时,命令前面加
`OS_DATABASE_DRIVER=…` 与
`OS_DATABASE_AUTH_TOKEN="${OS_DATABASE_AUTH_TOKEN:?…}"`。token
从不进入守卫、从不打印,未导出时 shell 直接停下。`os dev` 对非 SQLite
库自己打印的那行改为指向守卫给出的两条命令,不再写一条不带库名的。
- **为什么改**: 仪式只认 `--database-url` 和环境变量 `OS_DATABASE_DRIVER` /
`OS_DATABASE_AUTH_TOKEN`;这两个 flag 只把值放进子进程环境,照抄命令会规划项目默认库、换驱动或缺 token
(#31547,来自 #31546 的两条验收备注)。
- **风险与回滚**: 只改拒绝文案与父子进程间两个内部环境变量;接受/拒绝条件不变。不带这两个 flag、不用 `os dev -d`
时打印内容与 #31546 一致 (CONTROL 钉住)。回滚即 revert 本 PR。
- **先看**: `packages/runtime/src/organization-ownership-boot-guard.ts` 的
`connectionAssignments` /
`authTokenNote`;`packages/cli/src/utils/internal-ceremony-database-channel.ts`
的 `writeCeremonyConnectionChannel`;`dev.ts` 写通道的两处调用。
- **两分钟验证**: `cd packages/runtime && npx vitest run
src/organization-ownership-boot-guard.test.ts`,预期 30 个用例全过 (在 main 上 3
个红)。
- **需要你做的**: 无,席位落地。
## What changes
1. **`os dev -d` names the database, through the same channel.**
`dev.ts` now writes `OS_INTERNAL_CEREMONY_DATABASE_URL` with
`ceremonyDatabaseUrl(resolvedDb, { homeFlag: undefined })`, the function
`os start` calls, from the resolution the boot serves. `-d` (and
`--fresh`'s throwaway file) is `source: 'explicit'`, so it is named;
`$OS_DATABASE_URL`, a declared datasource and the default file are not,
because the ceremony's ladder reaches them. The `serve` child already
hands the channel to the guard's `databaseUrl`, so the `--dev` commands
read `os migrate organization-ownership --dev --database-url file:A.db
…`, and a credential URL is read from `"${OS_DATABASE_URL:?…}"` as
#31546 does. No second resolver.
2. **`os dev`'s own line points at those commands.** For a database it
does not upgrade in place (not a local SQLite file), and after a refused
upgrade plan, `os dev` printed "Run `os migrate organization-ownership
--dev` …", a second spelling with no database. It now says "Run the two
commands the boot guard's refusal above names, first the plan (review
it), then the apply, and start os dev again." The refusal stays the one
place that spells the commands.
3. **The driver and the token reach the ceremony.** First, the flags:
`os migrate organization-ownership` accepts `--database-url` and no
`--database-driver` / `--database-auth-token`. It reads both from the
environment (`resolveExplicitDriver` reads `OS_DATABASE_DRIVER`,
`resolveDatabaseAuthToken` reads `OS_DATABASE_AUTH_TOKEN`), and that is
exactly where `os start` / `os dev` put the two flags' values for their
child. So no flag is added; see Acceptance notes for the four-axis
reading. The printed commands now set them in front:
- `OS_DATABASE_DRIVER=KIND` when `--database-driver` chose the driver
(the value is from the flag's allowlist and is printed as one shell
word);
- `OS_DATABASE_AUTH_TOKEN="${OS_DATABASE_AUTH_TOKEN:?export
OS_DATABASE_AUTH_TOKEN as the database auth token this server was
started with}"` when `--database-auth-token` supplied the token, plus a
sentence saying the token is not printed. POSIX `${VAR:?msg}` stops the
shell when the variable is unset or empty, as #31546's credential rule
does for the URL.
- Order: driver, token, then #31546's `OS_AUTH_SECRET="$(cat FILE)"`,
then the command, `--dev` and `--database-url`.
4. **The token never reaches the guard.** New channel variables, same
ownership rule as #31546's (set when the flag was given, deleted
otherwise): `OS_INTERNAL_CEREMONY_DATABASE_DRIVER` carries the driver,
`OS_INTERNAL_CEREMONY_AUTH_TOKEN_FROM_FLAG` carries `1`, never the
token. `writeCeremonyConnectionChannel` is called by both `start.ts` and
`dev.ts`; `serve.ts` spreads `readCeremonyConnectionChannel` into the
guard's options (`databaseDriver`, `databaseAuthTokenFromFlag`). A
driver or token the operator exported is already in their shell and is
not repeated.
5. Changeset
`31547-ownership-refusal-names-dev-database-and-connection.md`:
`@objectstack/cli` and `@objectstack/runtime` patch (ruling A).
## Tests
- `packages/runtime/src/organization-ownership-boot-guard.test.ts`:
- ⭐ a `--database-driver` driver is set in front of both commands in
every refusal state, before the exported secret, and before `--dev` on a
development boot;
- ⭐ a token from `--database-auth-token` is read as
`${OS_DATABASE_AUTH_TOKEN:?…}` in both commands, with the note, alone or
beside a driver;
- ⭐ the printed plan command runs as written through `sh` with `os`
stubbed: with the token exported, the stub sees the driver, the token
and `--dev --database-url file:A.db`; with it unset, the shell exits
non-zero before `os` runs and says what to export;
- CONTROL: with neither option, the commands are as #31488 printed them
(the #31488 cases are unchanged and green).
- `packages/cli/src/utils/internal-ceremony-database-channel.test.ts`: ⭐
the driver is carried by value and the token only as `1` (the token's
text appears nowhere in the env); the parent owns both variables; ⭐
`start.ts` and `dev.ts` both write both channels from their own
resolution and flags.
- `packages/cli/src/utils/dev-organization-ownership-upgrade.test.ts`: ⭐
the non-SQLite line points at the refusal's commands and no longer
spells a bare `--dev` command; the refused-plan line likewise (an edited
assertion).
-
`packages/cli/test/dev-organization-ownership-stale-column.integration.test.ts`
(built CLI, the existing `os dev --no-watch -d file:A.db` run, no new
spawn): ⭐ the refusal the refused boot printed names `--dev
--database-url file:A.db` in both commands.
-
`packages/cli/test/start-organization-ownership-refusal.integration.test.ts`
(built CLI): a `-d file:A.db --database-driver sqlite
--database-auth-token …` boot. ⭐ Both commands are
`OS_DATABASE_DRIVER=sqlite OS_DATABASE_AUTH_TOKEN="${…:?…}"
OS_AUTH_SECRET="$(cat H/auth-secret)" os migrate organization-ownership
--database-url file:A.db …`, and the token is nowhere in the output. Run
verbatim with the token exported, the plan is of `A.db`. ⭐ With it
unset, the shell stops with the message and no plan file is written. It
runs before the `-d` apply, so #31546's controls are judged on the same
fixture as before.
**Red on main** (`4d165acd5e` source, these test files, every package
built from it):
- guard: `Tests 3 failed | 27 passed (30)`; the three ⭐ #31547 cases
fail and the CONTROL passes.
- cli unit: `Tests 5 failed | 15 passed (20)`
(`writeCeremonyConnectionChannel is not a function`; `start: expected …
to match /writeCeremonyConnectionChannel…/`; both `os dev` lines
`expected 'This development database is not a lo…' to contain 'the two
commands the boot guard's re…'`).
- end to end: `Tests 3 failed | 14 passed (17)`, all #31488 cases and
CONTROLs green. `os dev -d file:A.db` printed `Run 'os migrate
organization-ownership --dev --out PLAN' to write and review its plan,
…`, and the plan run with the token unset wrote
`plan-connection-unset.json`. The case "run verbatim with the token
exported, the plan is of the refused database" is green on main too
(main names `--database-url file:A.db` already), so it carries no ⭐.
**Ablations** (`scripts/ablation-replace.mjs`, each restored with the
blob equal to HEAD and `git diff HEAD` empty):
- guard driver (`if (false && opts.databaseDriver) words +=`): `Tests 3
failed | 27 passed (30)`, `expected '|tok-31547|migrate
organization-owner…' to be 'turso|tok-31547|migrate organization-…'`.
- guard token (`if (false && opts.databaseAuthTokenFromFlag) words +=`):
`Tests 2 failed | 28 passed (30)`; the run-as-written case reads
`expected +0 not to be +0` (the shell no longer stops).
- `os dev`'s hand-off, end to end through `dist/` (channel written as
`undefined` behind `"ABL31547D".length < 0`; preflight: marker present
in 1 built file): dev file `Tests 1 failed | 5 passed (6)`, the ⭐ #31547
case.
- `os start`'s driver/token hand-off, end to end through `dist/`
(`"ABL31547S".length > 0 ? { databaseDriver: undefined,
databaseAuthToken: undefined } : …`; marker present in 1 built file):
start file `Tests 2 failed | 9 passed (11)`, the two ⭐ cases.
- Restore leg: cli rebuilt from HEAD, `ablation-dist-preflight --absent`
for both markers: "marker absent from all 904 built files", "working
tree clean against HEAD".
## Acceptance notes
- **No `--database-driver` / `--database-auth-token` flag on the
ceremony** (the order asked for this to be decided on the four axes).
Business need, measured: the ceremony already reads `OS_DATABASE_DRIVER`
and `OS_DATABASE_AUTH_TOKEN`, which is where both flags of `os start` /
`os dev` land, so the printed commands can carry them without new
surface. Long-term: one way in per input; a second spelling on the
ceremony would need its own precedence rules against the variables. AI
mistakes: a token passed as a flag sits in argv (`ps`, shell history)
and invites printing it; the variable form lets the refusal never hold
the value, and `${…:?…}` refuses loudly. Startup scope: no new
capability is needed for the fix. Not added and not filed.
- **A token flag without a libSQL database** (for example
`--database-auth-token` beside a `postgres://` URL, where the stack
ignores it) still makes the commands require `OS_DATABASE_AUTH_TOKEN`.
The rule follows the flag, not the driver, so it stays one simple rule;
the cost is one export the ceremony would not need.
- **`os serve` run directly** has no database flags (it reads
`OS_DATABASE_URL` / `OS_DATABASE_DRIVER` from the operator's
environment), so the ceremony finds the same database. Nothing to do
there.
- The `os dev` end-to-end pin uses `-d file:A.db` (SQLite), because the
existing run already boots it and a non-SQLite database needs a server
in CI. The channel carries any URL the same way, and the non-SQLite text
is pinned at unit level.
## Verification
- Head `d2d02202`; origin/main `6ed533f6b6` merged before opening (it
touched `packages/spec/src/api/error-code-ledger.zod.ts`, no file of
this diff). The full `./packages/**` build ran after the merge.
- Pins on the merged head, after that build: guard `Tests 30 passed
(30)`, cli unit `Tests 20 passed (20)`, end to end `Tests 17 passed
(17)`.
- `pnpm typecheck` in `packages/runtime` and `packages/cli` (`tsc
--noEmit` and `check:test-typecheck`): exit 0. eslint on the 11 touched
`.ts` files: exit 0.
- Gates from `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`: 72 derived, 72 run, all exit 0 (`--ran` on
the record with exit codes: 0 NOT-MEASURED, 0 UNRUN, a derived zero).
One was red on the first pass and fixed in this branch:
`check:test-source-alias` asked the edited
`dev-organization-ownership-upgrade.test.ts` to load
`@objectstack/runtime` and `@objectstack/service-datasource` at module
top (the telemetry sibling's resolution reaches both through dynamic
imports); after it, the gate, that test file (13 passed),
`check:cross-package-test-inputs`, `check:cli-test-child-env`,
`check:undeclared-dep-imports`, `check:type-check-coverage` and the cli
typecheck all exit 0. Record:
`/mnt/project-files/pm-orders/server/31547-gates-ran.txt`.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(not touched)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes. (eslint on the 11 touched
`.ts` files: exit 0)
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
(`.changeset/31547-ownership-refusal-names-dev-database-and-connection.md`,
`@objectstack/cli` + `@objectstack/runtime` patch)
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (not touched)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English. (the 维护者速读 block is
the lane's required Chinese summary)
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01FEkQ5mbAyTfp7gbjowRA48
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FEkQ5mbAyTfp7gbjowRA48)_
<!-- signed: domain:server#5 · os-project-manager ·
session_01FEkQ5mbAyTfp7gbjowRA48 -->
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 04127cc commit c0fb794
12 files changed
Lines changed: 378 additions & 21 deletions
File tree
- .changeset
- packages
- cli
- src
- commands
- utils
- test
- runtime/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
45 | 50 | | |
46 | 51 | | |
47 | 52 | | |
| |||
841 | 846 | | |
842 | 847 | | |
843 | 848 | | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
844 | 860 | | |
845 | 861 | | |
846 | 862 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
| 17 | + | |
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| |||
4218 | 4218 | | |
4219 | 4219 | | |
4220 | 4220 | | |
4221 | | - | |
| 4221 | + | |
| 4222 | + | |
4222 | 4223 | | |
4223 | 4224 | | |
4224 | 4225 | | |
4225 | 4226 | | |
4226 | 4227 | | |
| 4228 | + | |
4227 | 4229 | | |
4228 | 4230 | | |
4229 | 4231 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
26 | | - | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
27 | 31 | | |
28 | 32 | | |
29 | 33 | | |
| |||
507 | 511 | | |
508 | 512 | | |
509 | 513 | | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
510 | 520 | | |
511 | 521 | | |
512 | 522 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
17 | 20 | | |
18 | 21 | | |
19 | 22 | | |
| |||
105 | 108 | | |
106 | 109 | | |
107 | 110 | | |
108 | | - | |
109 | | - | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
110 | 115 | | |
111 | 116 | | |
112 | 117 | | |
| |||
140 | 145 | | |
141 | 146 | | |
142 | 147 | | |
143 | | - | |
| 148 | + | |
144 | 149 | | |
145 | 150 | | |
146 | 151 | | |
147 | 152 | | |
148 | | - | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
149 | 158 | | |
150 | 159 | | |
151 | 160 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
227 | 227 | | |
228 | 228 | | |
229 | 229 | | |
230 | | - | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
231 | 236 | | |
232 | | - | |
233 | | - | |
| 237 | + | |
| 238 | + | |
234 | 239 | | |
235 | 240 | | |
236 | 241 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
| 25 | + | |
| 26 | + | |
24 | 27 | | |
25 | 28 | | |
| 29 | + | |
26 | 30 | | |
| 31 | + | |
27 | 32 | | |
28 | 33 | | |
29 | 34 | | |
| |||
63 | 68 | | |
64 | 69 | | |
65 | 70 | | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
23 | 31 | | |
24 | 32 | | |
25 | 33 | | |
26 | 34 | | |
27 | 35 | | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
28 | 40 | | |
29 | 41 | | |
30 | | - | |
31 | | - | |
| 42 | + | |
| 43 | + | |
32 | 44 | | |
33 | 45 | | |
34 | 46 | | |
| |||
55 | 67 | | |
56 | 68 | | |
57 | 69 | | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
33 | | - | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
34 | 38 | | |
35 | 39 | | |
36 | 40 | | |
| |||
248 | 252 | | |
249 | 253 | | |
250 | 254 | | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
251 | 266 | | |
252 | 267 | | |
253 | 268 | | |
| |||
0 commit comments