Repository navigation
Commit 47c3e12
fix(cli)!: os plugin install and the boot loader parse the compiled manifest through ManifestSchema (#27791)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} -->
_Requested by **Elon** · [project
thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DTirkaM35ZFp9q361oePFGn)_
Fixes #27686
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Skills-docs: follow-up owed — packages/cli/README.md (its `os plugin
install` paragraph lists the refusals by name and does not yet name
`manifest_invalid`; outside this card's declared file surface). No skill
under `skills/**` teaches the compiled-manifest read.
## 维护者速读
- 改了什么:`os plugin install` 和启动时加载已安装插件的那一步,现在都用同一个函数把 `.osplugin`
里的编译清单按 `ManifestSchema` 校验;`os plugin build` 拒收原生插件时会写出是谁把它引进来的。
- 为什么改:旧版本打出的包(比如带已退役的 `packaging: 'manifest-deps'`)以前照装不误,到启动时才以
`import_failed` 失败,看不到退役处方;现在安装时就被拒,并给出 schema 自己的处方。
- 风险与回滚:这是收窄:清单不合 schema 的包(缺
`name`/`type`、多余键等)从此装不上,启动时也会被逐个点名拒载(不致命,其它插件照常加载);回滚即回退本 PR。
- 先看:`packages/runtime/src/compiled-plugin-manifest.ts`(唯一的解析函数)→
`packages/cli/src/commands/plugin/install.ts` 的清单读取段 →
`packages/runtime/src/installed-plugin-loader.ts` 第 4 步。
- 两分钟验证:`pnpm --filter @objectstack/cli exec vitest run
src/commands/plugin/install.integration.test.ts -t manifest-deps`,预期看到
`manifest_invalid` 拒装且 details 首行是 `packaging: ` 加退役处方。
- 需要你做的:无,席位落地(另需一次契约复审 PASS)。
## What changes
- **One parse helper, both doors.** `readCompiledPluginManifest` (new,
`packages/runtime/src/compiled-plugin-manifest.ts`, exported from
`@objectstack/runtime`) reads `objectstack.plugin.json` out of an
artifact's files and parses it through `ManifestSchema`. It answers the
parsed manifest, or `unreadable` (no manifest, not a JSON object) /
`invalid` (the schema's issues, one `path: message` line each, plus the
same on one line).
- **`os plugin install`** asks it at step 2, before integrity and before
anything is unpacked. `invalid` is refused as `manifest_invalid` with
the issues as `details`; for `packaging: 'manifest-deps'` the first line
is the retirement prescription. `unreadable` keeps its
`manifest_unreadable` refusal. The hand-rolled `id`/`version` check
(`manifest_incomplete`) and the hand-rolled `integrity` shape check
(`integrity_malformed`) are gone: both are schema issues now, so there
is no second copy of either rule. The record's `runtime` /
`grantedPermissions` come from the parsed manifest.
- **The boot loader** (`installed-plugin-loader.ts`, check 4) asks the
same helper. `invalid` refuses that plugin as `manifest_invalid` (new
member of `InstalledPluginRefusalCode`) with the schema's message,
before its entry module is imported; the boot stays non-fatal and the
next plugin loads. `unreadable` keeps `artifact_unreadable`.
- **`os plugin build`'s native-addon refusal** names, for a bundled
package that builds or ships an addon, the files outside it that import
it and the shortest chain from the plugin's own source (`src/index.ts →
ws-like → fake-native`), the way the `.node`-module line already names
its importers.
- Changeset: `@objectstack/cli` and `@objectstack/runtime` minor,
`fix(cli)!`, BREAKING banner, `adr-0087: not-required
(no-migration-prescription)`.
## Tests
- `packages/runtime/src/installed-plugin-loader.test.ts`: two new table
rows (`manifest-deps` → `manifest_invalid`; a manifest with no `type` →
`manifest_invalid`), each asserting nothing was imported; and one case
asserting the refusal message carries the prescription (`packaging:
\`packaging: 'manifest-deps'\` was removed … Delete the key`) while the
next plugin is imported and loaded. Fixture manifest gains `type:
'plugin'` (a valid manifest).
- `packages/cli/src/commands/plugin/install.integration.test.ts`: a
signed, trusted `manifest-deps` artifact is refused as
`manifest_invalid`, `details` opens with the prescription, and nothing
exists under `plugins/env_local/com.acme.retired`; a drifted manifest
(no `name`, an array `integrity`) is refused naming both issues. Fixture
gains `name`.
- `packages/cli/test/serve-installed-plugins.integration.test.ts` (real
`os serve` boots): a pre-retirement install laid down as an
id/version-only install did is refused at boot as `manifest_invalid`
with the prescription, not `import_failed`, its `init` never runs, and
the boot exits 0. Fixture gains `name`.
- `packages/cli/test/plugin-build-bundled-only.test.ts`: a transitive
native package (`src/index.ts → ws-like → fake-native`, `binding.gyp`)
is refused naming `imported by node_modules/ws-like/lib/buffer-util.js`
and `reached through src/index.ts → ws-like → fake-native`, and
`ws-like` is never named as a native package.
- `json-stdout-purity.e2e.test.ts` and
`schema-migrate.one-shot-family.integration.test.ts` fixtures gain
`name` (their artifacts now meet `ManifestSchema`).
**Ablation** (each fix removed on disk, the pins re-run, then restored
with `git checkout HEAD --`, tree clean):
- loader parse replaced by a bare `JSON.parse` → 3 failed / 56 passed
(the three new loader pins); restored → 59 / 59.
- install parse replaced by a bare `JSON.parse`, and the build's
`packageImporters` call replaced by empty lists → 3 failed / 31 passed
(the two install pins + the chain pin); restored → 34 / 34.
## Acceptance notes
- `ManifestSchema` is consumed as it stands; `packages/spec` is not
touched.
- The ADR-0087 D2 conversion `manifest-packaging-manifest-deps-removed`
could strip the value from a stored artifact, but the triage ruling is
that both doors refuse with the schema's message, and a conversion at
install would hand the operator bytes that differ from the signed
artifact; neither door converts.
- An artifact `os plugin build` produced from a valid source manifest
re-parses unchanged (the boot test's `os plugin build` artifact installs
and loads, `com.acme.built`).
## Verification
Local readings on `objectstack-ai/objectstack` at `f6d16b37` (base
`origin/main` `81d00a4e`):
- `pnpm --filter @objectstack/runtime typecheck`: exit 0. `pnpm --filter
@objectstack/cli typecheck` (tsc + test typecheck): exit 0.
- `packages/runtime` full `vitest run`: Test Files 482 passed (482),
Tests 8767 passed / 19 skipped.
- `packages/cli` full `vitest run` (unit + integration, after `pnpm
--filter @objectstack/cli build`): Test Files 532 passed / 1 skipped
(533), Tests 6637 passed / 9 skipped.
- `packages/cli` nightly tier, `OS_TEST_TIERS=nightly`:
`test/json-stdout-purity.e2e.test.ts` 77 / 77.
- Gates: `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derives 61 families; every one run with its
exit code recorded before any pipe, plus `pnpm lint` (exit 0). `--ran`
reconciliation: `61 derived famil(ies) accounted for — 59 run, 2
NOT-MEASURED`, 0 unrun. 59 exit 0.
- NOT MEASURED: `pnpm check:dual-build-cjs-loads` (exit 3, needs every
package's dist), `pnpm check:i18n-coverage` (exit 3, needs the example
closure it prints). CI runs both.
- Dogfood battery (owed by the `(narrowing)` arm): run on the final
head, reported in the card's report comment.
---
_Generated by [Claude
Code](https://claude.ai/code/session_016WV4czY8bzmjKmuhj1GP9c)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_016WV4czY8bzmjKmuhj1GP9c
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 359e935 commit 47c3e12
16 files changed
Lines changed: 305 additions & 56 deletions
File tree
- .changeset
- docs/qa/platform-checklist/areas
- packages
- cli
- src
- commands/plugin
- utils
- test
- runtime/src
- spec
- liveness
- src/kernel
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1716 | 1716 | | |
1717 | 1717 | | |
1718 | 1718 | | |
1719 | | - | |
| 1719 | + | |
1720 | 1720 | | |
1721 | 1721 | | |
1722 | 1722 | | |
| |||
1799 | 1799 | | |
1800 | 1800 | | |
1801 | 1801 | | |
1802 | | - | |
| 1802 | + | |
1803 | 1803 | | |
1804 | 1804 | | |
1805 | 1805 | | |
| |||
1838 | 1838 | | |
1839 | 1839 | | |
1840 | 1840 | | |
| 1841 | + | |
| 1842 | + | |
| 1843 | + | |
| 1844 | + | |
| 1845 | + | |
| 1846 | + | |
1841 | 1847 | | |
1842 | 1848 | | |
1843 | 1849 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
155 | 155 | | |
156 | 156 | | |
157 | 157 | | |
158 | | - | |
| 158 | + | |
159 | 159 | | |
160 | 160 | | |
161 | 161 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
137 | 137 | | |
138 | 138 | | |
139 | 139 | | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
140 | 177 | | |
141 | 178 | | |
142 | 179 | | |
143 | 180 | | |
144 | 181 | | |
145 | | - | |
| 182 | + | |
| 183 | + | |
146 | 184 | | |
147 | 185 | | |
148 | 186 | | |
| |||
158 | 196 | | |
159 | 197 | | |
160 | 198 | | |
161 | | - | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
162 | 206 | | |
163 | 207 | | |
164 | 208 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
112 | | - | |
| 112 | + | |
113 | 113 | | |
114 | 114 | | |
115 | 115 | | |
116 | 116 | | |
| 117 | + | |
117 | 118 | | |
118 | 119 | | |
119 | 120 | | |
120 | 121 | | |
| 122 | + | |
121 | 123 | | |
122 | 124 | | |
123 | 125 | | |
| |||
330 | 332 | | |
331 | 333 | | |
332 | 334 | | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
333 | 360 | | |
334 | 361 | | |
335 | 362 | | |
| |||
0 commit comments