You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit c2bc76e
Browse filesBrowse the repository at this point in the historyBrowse files
fix(cli): os serve names an unloaded devPlugins entry by the list it was written in (#31356)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1X1zG3Xn4JonaKvDLLr5gnjL)_
Refs #28284
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:`os serve --dev` 因可能提供加密提供者的项没能加载而拒绝启动时,`devPlugins` 里的项现在点名为
`devPlugins[i]`,不再按它排在 `plugins` 之后的位置点名。判断是否拒绝的 `providesServices`
读取改为与 `cryptoProviderPlugins` 共用同一个判定,docstring
也写明了「声明其它服务或什么都没声明的项被排除」。
- **为什么改**:#31263 契约复审的 should-fix 2 与 3(should-fix 1 的 docstring 部分)。
- **风险与回滚**:只改 `@objectstack/cli`(patch)。哪些项会被拒绝不变,只改报错里的名称。回滚即 revert 本
PR。
- **先看**:`packages/cli/src/commands/serve.ts` 的
`declaresCryptoProvider`、`refuseUnloadedCryptoProviderPlugins` 的
label,以及栈插件循环 catch 里的 `list: 'devPlugins'`。
- **两分钟验证**:`pnpm --filter @objectstack/cli exec vitest run --project
integration src/commands/serve-host-crypto-provider.test.ts
test/serve-crypto-provider-plugin-load-failure.integration.test.ts`,预期
34/34。
- **需要你做的**:无,席位复审后落地。
## What changes
- **Refusal label.** `os serve` refuses to boot when an entry that may
provide the crypto provider (`crypto-provider`) does not load. Under
`--dev`, a `devPlugins` entry is now named `devPlugins[i]`, where it
used to be named by its position after the stack's `plugins`
(`plugins[plugins.length + i]`).
- `UnloadedStackPlugin` gains an optional `list` (`'plugins' |
'devPlugins'`; absent means `plugins`).
- The boot loop records it from the authored `plugins` length.
- **One reading of the declaration.**
`refuseUnloadedCryptoProviderPlugins` reads `providesServices` through
the predicate `cryptoProviderPlugins` filters by
(`declaresCryptoProvider`), not through its own copy.
- **Docstring.** It now states the rule the code applies: an entry that
declares other services, or declares none, is ruled out.
Which entries are refused is unchanged.
## Why
Follow-up to the contract review of #31263 (should-fixes 2 and 3, and
the docstring half of 1).
## Verification
Readings on head `279bb7dca` (one commit on `main` at `9447f9552`).
<details><summary>Pins, red-first and restore</summary>
- `serve-crypto-provider-plugin-load-failure.integration.test.ts`: a
fifth real boot, `os serve --dev` over a host config whose `plugins[1]`
and `devPlugins[0]` are specifiers that do not import. It is refused,
and names `devPlugins[0] ('…')` and `plugins[1] ('…')`.
- `serve-host-crypto-provider.test.ts`:
- a unit case for the `devPlugins` label;
- a case that refusal and `cryptoProviderPlugins` agree on a declaring
entry, an entry declaring another service, and an entry declaring none;
- the source pin now locates the record by `unloadedStackPlugins.push(`.
- Red-first: with `serve.ts` set back to its `main` blob, the two files
read 2 failed | 32 passed (34). The two red cases were the `devPlugins`
label unit case and the `--dev` boot. Restored (blob equal to HEAD): 34
passed (34).
</details>
<details><summary>Package suite and typecheck</summary>
- `tsc --noEmit` in `packages/cli`: exit 0.
- `pnpm --filter @objectstack/cli test` under the verify lock: Test
Files 608 passed | 1 skipped (609), Tests 7538 passed | 9 skipped
(7547), `VERDICT command-exit 0`.
</details>
<details><summary>Derived gates</summary>
`node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands`: 72 families on this diff, every one run and exit 0
(`check:type-check-debt` under the verify lock). `--ran` reconciliation:
72 derived, 72 run, 0 NOT-MEASURED, 0 UNRUN.
</details>
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(No `packages/spec` change.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `eslint --no-inline-config` on the three
changed source 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.
- [x] 6. `packages/spec` touched: 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, except the maintainer
summary the lane's PRs carry.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FEkQ5mgePB9NQ4PNhvYVxk)_
Co-authored-by: Claude <noreply@anthropic.com>
fix(cli): `os serve` names an unloaded `devPlugins` entry by the list it was written in
6
+
7
+
Clause-②: no
8
+
Metadata-protocol: none
9
+
10
+
When `os serve --dev` refuses to boot because an entry that may provide the crypto provider (`crypto-provider`) did not load, a `devPlugins` entry is now named `devPlugins[i]` instead of by its position after the stack's `plugins`. Which entries are refused is unchanged: the refusal reads an entry's `providesServices` through the same predicate as the rest of the crypto-provider checks.
Copy file name to clipboardExpand all lines: packages/cli/test/serve-crypto-provider-plugin-load-failure.integration.test.ts
+19-3Lines changed: 19 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,5 @@
1
1
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2
-
// test-budget: 40 — spawns `os serve` (src, via runServe) once per single-cause fixture: four boots, two refused and two that reach the ready banner; budget to be re-measured
2
+
// test-budget: 40 — spawns `os serve` (src, via runServe) once per single-cause fixture: five boots, three refused and two that reach the ready banner; budget to be re-measured
3
3
4
4
/**
5
5
* `os serve` refuses to boot when a `plugins` entry fails to load and it cannot
@@ -14,6 +14,8 @@
14
14
* nothing says what it provides, so the boot is refused;
15
15
* - an entry that materializes, declares the crypto provider in
16
16
* `providesServices`, and is refused by the kernel: refused;
17
+
* - under `--dev`, a `devPlugins` entry that does not materialize: refused,
18
+
* named by the list it was written in (`devPlugins[0]`);
17
19
* - CONTROL: an entry that materializes, declares nothing of the kind, and is
18
20
* refused by the kernel: the boot goes on, as before, and says so;
19
21
* - CONTROL: a config whose every entry loads boots.
0 commit comments