Repository navigation
Commit fe9e856
<!-- claude-projects-attribution: {"github_login":"os-sales"} -->
_Requested by **Sales** · [project
thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuPMZ8BHWtPf6nBTp51qSM64)_
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
Part of #27123 (slice 3)
Ruling-ref: 6467932167
Clause-②: no (narrowing) — `PluginPackagingSchema` drops
`manifest-deps`, so every door that parses a plugin manifest now refuses
a value it accepted, and `os plugin build` refuses a native `.node`
addon it used to pack (or fail on, unnamed)
Metadata-protocol: narrowed
Skills-docs: none — no skill under `skills/**` and no docs page outside
the generated references teaches `manifest-deps` or a native addon.
`content/docs/protocol/kernel/plugin-spec.mdx` lists `packaging` among
the optional manifest keys (still true) and its `.osplugin` format list
already names no `package.json`; the five generated reference pages are
regenerated by `gen:docs`
## 维护者速读
- **改了什么:** 插件清单的 `packaging` 只剩 `bundled` 一个值。`manifest-deps`
改为退役值:所有解析插件清单的入口(`os plugin build` 读的
`objectstack.plugin.json`、`defineStack` 的 `manifest`、制品的
`packages[].manifest`、`POST /packages` 安装请求体)都按名拒绝它并给出迁移说明,TypeScript
也不再接受。`os plugin build` 删掉了 `manifest-deps` 的两个分叉(依赖外置、打包 `package.json`
和锁文件),并在写制品之前拒绝原生 `.node` 插件:入口导入的 `.node` 文件、带 node-gyp 构建或自带 `.node`
的依赖包、`assets/` 下的 `.node` 文件,逐个点名,不留制品。配套 D2 转换、D3 条目、liveness 行(改为
`planned`)、平台清单条目和参考页。
- **为什么改:** 这是 #27123 裁决 B′ 第 3 片的 server 半(ADR-0025 v2
§3.3、Q6)。`manifest-deps`
构建出的制品把依赖留在包外,但从来没有任何环节去安装它们,插件加载必然缺依赖;原生插件既不能打进 bundle,也不是每个宿主都能运行。
- **风险与回滚:** 写了 `packaging: 'manifest-deps'`
的清单会被拒绝;仓库内没有任何来源这样写(树级缺席扫描为零)。stack 源码可用 `os migrate meta --from 17
--write` 自动删掉这个键,`objectstack.plugin.json` 需手工删。带原生插件的插件不能再构建。已声明
`Clause-②: no (narrowing)`、`Metadata-protocol: narrowed`,changeset 带
BREAKING 横幅和 ADR-0087 `registered` 处置,spec 为 minor、cli 为 patch。回滚 =
revert 本 PR。
- **先看:** `packages/cli/src/commands/plugin/build.ts` 的
`findNativeAddons` 和拒绝块;`packages/spec/src/kernel/manifest.zod.ts` 的
`PACKAGING_RETIRED` /
`PluginPackagingSchema`;`packages/spec/src/conversions/registry.ts` 的
`manifestPackagingManifestDepsRemoved`;`packages/spec/liveness/manifest.json`
的 `packaging` 行。
- **两分钟验证:** 运行 `pnpm --filter @objectstack/cli exec vitest run
--project unit test/plugin-build-bundled-only.test.ts`,预期 9/9 通过(它从 spec
的 `dist/` 读 `ManifestSchema`,先 `pnpm --filter @objectstack/spec
build`);再运行 `pnpm --filter @objectstack/spec exec vitest run --project
repo src/kernel/manifest-packaging-retirement.test.ts`,预期 15/15 通过。
- **需要你做的:** 现在无需操作。按分诊 6498767357
的跨域例外,批准前要先完成契约审查(#27345),由席位安排;审查通过后由席位落地。
## What changes
**`@objectstack/spec`**
- `PluginPackagingSchema` is `enumWithRetiredValues(['bundled'],
PACKAGING_RETIRED)`. `manifest-deps` is refused at `packaging` with its
prescription (removed in 18.3.0; delete the key; `os migrate meta --from
17` for a stack source; an `objectstack.plugin.json` is edited by hand)
at every door that parses a manifest: `ManifestSchema`, a stack's
`manifest` through `defineStack` (`STACK_SCHEMA_INVALID`, path
`manifest.packaging`), `ArtifactPackageEntrySchema`
(`packages[].manifest`), and the install body
(`PackageInstallManifestSchema` extends `ManifestSchema`). The value is
out of the input type, so authoring it is a compile error.
- ADR-0087 D2 conversion `manifest-packaging-manifest-deps-removed`
(`toMajor: 18`, `retiredFromLoadPath: true`, `retiredAfter: '18.2.0'`,
order 80): deletes `packaging: 'manifest-deps'` from a stack's
`manifest` and from each `packages[].manifest`, one notice each (`to:
'(removed)'`), copy-on-write, idempotent, with its fixture.
- D3 entry `manifest-packaging-manifest-deps-retired` naming the
conversion, and its `STEP18_RATIONALE` fragment (order 114) in
`registry.ts.template`.
- `retired-key-removal-version.pin.test.ts` gains the conversion's JOIN
row.
- Liveness: the `packaging` row is `planned` on the one-member precedent
(`sharing_rule.type`, `connector.authentication`): nothing dispatches on
the value any more, and the refusal is real, so neither `live` nor
`dead`. The note keeps the history of the 2026-08-28 `live` grade, which
rested on the two forks this PR deletes, and the flip condition. The
`manifest` state-count shard is regenerated (`gen:liveness-counts`: live
29 → 28, planned 0 → 1).
- `vitest.repo-tests.json` lists the new pin: its tree-scoped absence
walk reads outside the package, over globs already declared in
`scripts/cross-package-test-inputs.mjs` and `turbo.json`.
**`@objectstack/cli`**
- `os plugin build`: the esbuild call externalizes `@objectstack/*`
only; the `packaging === 'manifest-deps'` externalize fork and the
`package.json` / `pnpm-lock.yaml` packing fork are gone. The `Loaded`
line still echoes `packaging` (default `bundled`).
- Native-addon refusal, before the integrity map and before anything is
written. An esbuild plugin loads every `.node` module as empty so the
graph completes, and the metafile is read for three signals: a `.node`
module the bundle reaches (named with its importers); a bundled package
(the directory after the last `node_modules` in an input path) that has
`binding.gyp`, `gypfile: true` or a shipped `.node` file; and a `.node`
file staged from `assets/`. Every hit is listed under `✗ Native addon
refused: …`, exit 1, no `.osplugin`.
- `osplugin.ts`: the archive-layout docblock no longer lists the
`manifest-deps` rows.
**Ledgers and generated pages**
- Platform checklist `cli.plugin-manifest-build-contract`, revision 4:
the fork clause becomes the bundled-only clause (with the
`manifest-deps` refusal), a native-addon clause and probes 6f–6h are
added, and steps, fixtures, knownGaps, negatives and sources follow. The
integrity and trust-tier negatives are untouched and were not
re-verified here.
- `gen:docs` regenerated `content/docs/references/{kernel/manifest,
kernel/package-registry, api/package-api, api/package-api-assembled,
api/protocol}.mdx`: `Enum<'bundled'>` and the new describe text. No
other generated artifact moved (`check:generated` names only these).
## Why
ADR-0025 v2 §3.3 (maintainer decision Q6, ruling B′) makes `bundled` the
only packaging strategy and disallows native addons, with M1 owing the
build refusal. `manifest-deps` produced an artifact that kept its
dependencies outside the bundle and carried `package.json` for an
install step that nothing ever ran (`os plugin install` and the
boot-time loader import `dist/index.mjs` as packed), so such a plugin
could not load its dependencies.
## Tests
Red first on `df778ab4` (tests only, through the real doors), then green
on the fix.
- `packages/cli/test/plugin-build-bundled-only.test.ts` (new, 9 cases)
drives `PluginBuild.run([dir])` over real on-disk projects with their
own `node_modules`. Every refusal asserts exit 1, the subject named, and
no artifact on disk.
- `bundled` and unset: the dependency is inlined, the archive is exactly
`SIGNATURE`, `dist/index.mjs` and the manifest, with no `package.json`
or lockfile and no surviving import.
- `packaging: 'manifest-deps'`: refused under `objectstack.plugin.json
is invalid`, the `✗` line names `packaging`.
- A `.node` file the entry imports (file and importer named); a
dependency whose `main` is `addon.node`; a bundled dependency with
`binding.gyp`, with `gypfile: true`, and with `prebuilds/…/fake.node`,
each locating its binary at run time (the package is named, the pure
dependency beside it is not); `assets/bin/addon.node` (named;
`assets/icon.svg` is not).
- `packages/spec/src/kernel/manifest-packaging-retirement.test.ts` (new,
15 cases, `repo` project): the vocabulary is exactly `bundled`; each
door refuses with the prescription (`ManifestSchema`,
`ArtifactPackageEntrySchema`, `defineStack`'s 422 envelope at
`manifest.packaging`), with a control that a near miss (`bundle`) keeps
zod's own message; `bundled` parses and absence stays absent;
`@ts-expect-error` at the authoring site; the D2 conversion (strips both
places, leaves `bundled`, notices, copy-on-write, idempotent, not
replayed by `normalizeStackInput`, wired into `MIGRATIONS_BY_MAJOR[18]`
with `retiredAfter: '18.2.0'`); the D3 entry names it; and a tree-scoped
absence walk over `packages`, `examples`, `skills`, `content`, `scripts`
(anti-vacuity: more than 1000 files visited, more than 3 `packaging:
'bundled'` mentions seen), with each structural exclusion's reason
stated.
- `retired-key-removal-version.pin.test.ts`: the JOIN row for the new
conversion.
## Acceptance notes
- **Disposition `registered`.** The changeset carries `<!-- adr-0087:
registered manifest-packaging-manifest-deps-removed,
manifest-packaging-manifest-deps-retired -->`. The narrowing has a
mechanical migration for a stack source, so `not-required` would
contradict the BREAKING FROM→TO. The conversion, the D3 entry, the
STEP18 fragment, the pin row and the checklist edit were added under the
seat's claim increment 6499637007, and the `vitest.repo-tests.json` line
under increment 6503048893.
- **Levels.** `@objectstack/spec` is `minor` (`Metadata-protocol:
narrowed` must grade at least one package `minor`); `@objectstack/cli`
is `patch` with the BREAKING banner, the slice-2 precedent for a
non-protocol narrowing.
- **Liveness `planned`, not `live` or `dead`.** Argued in the row's
note: no fork reads the value, the refusal is enforcement, and the ADR
keeps the key with one live value. The liveness README's `manifest`
notes cell names no `packaging` set, so it was left alone (it is outside
this PR's claim).
- **The native-addon refusal is conservative.** A bundled package that
ships a `.node` file it never loads on the plugin's code path is still
refused; the remedy in the message (replace or drop it) applies.
- **`os migrate meta` and `objectstack.plugin.json`.** The D2 conversion
runs over stack sources. An `objectstack.plugin.json` is not a source
`os migrate meta` reads, so the prescription tells the author to delete
the key there by hand.
- **Branch** is the card thread's harness-pinned
`claude/project-thread-c3umeu`, which claim 6499525158 names.
## Known limitations
- An addon loaded at run time from an absolute or computed path outside
every bundled package is invisible to the build. The checklist item
records it as a knownGap.
- `ui/packages/app-shell/src/console/marketplace/marketplaceApi.ts`
still types cloud listing data as `'bundled' | 'manifest-deps'`, and
`docs/design/plugin-distribution.md` and ADR-0025 mention
`manifest-deps`. All three are outside this slice (the ADR edit is the
Tier H half), and the absence walk does not reach `ui/`, `docs/` or
`.tsx`.
## Verification
Head `b0bfccdd` = fix `c349e372` + one merge of `origin/main`
(`b3b4af13`, merge base `efa2915e`) + the roster line. Heavy runs under
`scripts/pm/os-verify-lock.sh` (slot `issue-27123`,
`NODE_OPTIONS=--max-old-space-size=4096`).
<details><summary>Red, then green</summary>
- Red on `df778ab4` (fix reverted in the tree, spec `dist/` from before
the fix): spec pin `Tests 8 failed | 7 passed (15)` (`expected [
'bundled', 'manifest-deps' ] to deeply equal [ 'bundled' ]`, each door
accepting the value, the conversion and the D3 entry absent); cli pin
`Tests 7 failed | 2 passed (9)` (`manifest-deps` built an artifact; the
`.node` import failed as a bare `Bundle failed`; the run-time-located
and `assets/` addons built an artifact). The two `bundled`/unset
controls were green on `df778ab4` too.
- Green: spec pin `15 passed (15)` (`repo` project, final head); cli pin
`9 passed (9)` after the spec rebuild.
</details>
<details><summary>Suites and typecheck</summary>
- `@objectstack/spec` `--project local`, full, on the fix: 751 files
passed; the 24 failures were all in
`scripts/liveness/check-liveness.test.ts`, the stale `manifest` count
shard. After `gen:liveness-counts`: `check:liveness` exit 0 and
`scripts/liveness/` `12 passed (12)`, `359 passed (359)`.
- On the merged head: spec
`src/kernel/manifest-packaging-retirement.test.ts`, `src/shared/`,
`src/conversions/`, `src/migrations/`,
`scripts/conversions-major18-merge.test.ts`,
`scripts/step18-rationale-merge.test.ts`, `scripts/liveness/`: `81
passed (81)`, `2321 passed (2321)`. cli `plugin-build-bundled-only`,
`osplugin`, `plugin-sign`, `plugin-publish`: `25 passed (25)`.
- `@objectstack/cli` `--project unit`, full: `350 passed (350)`, `5063
passed (5063)`. `--project integration` on `serve-installed-plugins`,
`plugin/install` and `schema-migrate.one-shot-family`: `153 passed
(153)`. (A whole-package run of both projects exceeded the 30-minute
background limit and was stopped, so it is not counted.)
- `typecheck` of spec and cli, each with `check:test-typecheck`: exit 0.
- `@objectstack/spec` `test:repo` on the final head (the new pin now
runs here): `87 passed (87)`, `1448 passed (1448)`.
</details>
<details><summary>Ablations (scripts/ablation-replace.mjs, restored blob
== HEAD)</summary>
- **A, the native-addon refusal** (`if (nativeAddons.length > 0) {` →
`if (false && …)`, build.ts on `c349e372`): mutation landed (anchor 1 →
0) → cli pin `6 failed | 3 passed (9)`, exactly the six addon cases →
restored, blob `f5cb8ec5` == HEAD, `git diff HEAD` empty.
- **B, the narrowing** (`enumWithRetiredValues(['bundled'],
PACKAGING_RETIRED)` → `enumWithRetiredValues(['bundled',
'manifest-deps'], { 'ablation-27123-marker': … })`): spec pins `6 failed
| 68 passed (74)` (the vocabulary, the three doors, the authoring-site
case and the removal-version JOIN row); spec rebuilt,
`ablation-dist-preflight.mjs @objectstack/spec ablation-27123-marker` →
marker present in 2 built files, exit 0; cli pin `1 failed | 8 passed
(9)`, exactly the `manifest-deps` case → restored, blob `ed95910f` ==
HEAD, `git diff HEAD` empty; after the rebuild, `--absent` → marker
absent from all 580 built files, tree clean, exit 0.
</details>
<details><summary>Derived gates and the --ran reconciliation</summary>
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` → 111 commands, the same list on `b3b4af13`
and on the final head; each run with its exit captured before any pipe.
- Three were not green at first, and each was rerun green on the final
head: `check:cross-package-test-inputs` exit 1 (the new pin was missing
from `vitest.repo-tests.json`; fixed in `b0bfccdd` under increment
6503048893), `check-plugin-teardown-shape --self-test` exit 3 (its
pinned fixture commit was not in the shallow clone; fetched),
`check:dual-build-cjs-loads` exit 3 (six packages unbuilt; built).
`check-changeset-no-major`, `check-adr-0087-registration` and
`check-empty-changeset` were also rerun on the final head.
- `--ran`: `111 derived, 111 run, 0 NOT-MEASURED, 0 UNRUN`, all exit 0.
- `check-adr-0087-registration --base origin/main`: "1 declared-breaking
changeset(s), each carrying an ADR-0087 disposition …
[BREAKING+bang+clause-②-narrowing+metadata-protocol-narrowed] registered
manifest-packaging-manifest-deps-removed,
manifest-packaging-manifest-deps-retired".
- `check-changeset-no-major --base origin/main --event` with this body
as the `pull_request` payload: no `major`; LEVEL AXIS ✓ ("declares
clause-② `no (narrowing)` and metadata-protocol `narrowed`, and it
grades a package whose published source … it moves at `minor` or above":
`@objectstack/spec: minor`; `@objectstack/cli: patch` not refused), exit
0.
</details>
<details><summary>Dogfood, lint, checklist, merge-tree</summary>
- Dogfood battery (owed by the narrowing arm), once on the merged tree
with no `OS_TEST_SHARD`: closure built with `turbo run build
--filter='@objectstack/dogfood^...'` (68/68 tasks), then `pnpm --filter
@objectstack/dogfood test`: VERDICT `command-exit 0`, `351 passed | 3
skipped (354)` files, `2822 passed | 20 skipped (2842)` tests.
- `pnpm lint` (repo-wide, `--no-inline-config`): exit 0. eslint over
`packages/cli packages/spec`: exit 0.
- `check-platform-checklist`: full gate OK (352 items); `--changed-from
origin/main` OK.
- `check:generated`: only `content/docs/references/**` was stale,
regenerated with `gen:docs`; the thirteen source audits it names exit 0
(`check:skill-examples` once `@objectstack/client-react` was built,
`check:scripts-typecheck` inside the spec `typecheck`).
- Control-byte scan over the 19 changed files: no hit.
- `git merge-tree` against every open PR: none changes a file this PR
changes. 41 of 43 merge clean; #27246 and #27623 conflict with
`origin/main` itself, in files this PR does not touch.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L6bhgUCizoYWQVJWjhdR6F)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L6bhgUCizoYWQVJWjhdR6F
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent da7bcc8 commit fe9e856
19 files changed
Lines changed: 929 additions & 77 deletions
File tree
- .changeset
- content/docs/references
- api
- kernel
- docs/qa/platform-checklist/areas
- packages
- cli
- src
- commands/plugin
- utils
- test
- spec
- liveness
- state-counts
- src
- conversions
- kernel
- migrations
- entries/semantic
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
106 | 106 | | |
107 | 107 | | |
108 | 108 | | |
109 | | - | |
| 109 | + | |
110 | 110 | | |
111 | 111 | | |
112 | 112 | | |
| |||
289 | 289 | | |
290 | 290 | | |
291 | 291 | | |
292 | | - | |
| 292 | + | |
293 | 293 | | |
294 | 294 | | |
295 | 295 | | |
| |||
352 | 352 | | |
353 | 353 | | |
354 | 354 | | |
355 | | - | |
| 355 | + | |
356 | 356 | | |
357 | 357 | | |
358 | 358 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
162 | 162 | | |
163 | 163 | | |
164 | 164 | | |
165 | | - | |
| 165 | + | |
166 | 166 | | |
167 | 167 | | |
168 | 168 | | |
| |||
208 | 208 | | |
209 | 209 | | |
210 | 210 | | |
211 | | - | |
| 211 | + | |
212 | 212 | | |
213 | 213 | | |
214 | 214 | | |
| |||
316 | 316 | | |
317 | 317 | | |
318 | 318 | | |
319 | | - | |
| 319 | + | |
320 | 320 | | |
321 | 321 | | |
322 | 322 | | |
| |||
433 | 433 | | |
434 | 434 | | |
435 | 435 | | |
436 | | - | |
| 436 | + | |
437 | 437 | | |
438 | 438 | | |
439 | 439 | | |
| |||
566 | 566 | | |
567 | 567 | | |
568 | 568 | | |
569 | | - | |
| 569 | + | |
570 | 570 | | |
571 | 571 | | |
572 | 572 | | |
| |||
660 | 660 | | |
661 | 661 | | |
662 | 662 | | |
663 | | - | |
| 663 | + | |
664 | 664 | | |
665 | 665 | | |
666 | 666 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2113 | 2113 | | |
2114 | 2114 | | |
2115 | 2115 | | |
2116 | | - | |
| 2116 | + | |
2117 | 2117 | | |
2118 | 2118 | | |
2119 | 2119 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
65 | | - | |
| 65 | + | |
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
| |||
179 | 179 | | |
180 | 180 | | |
181 | 181 | | |
182 | | - | |
| 182 | + | |
183 | 183 | | |
184 | 184 | | |
185 | 185 | | |
186 | 186 | | |
187 | | - | |
188 | 187 | | |
189 | 188 | | |
190 | 189 | | |
| |||
0 commit comments