Repository navigation
fix(cli)!: os plugin install and the boot loader parse the compiled manifest through ManifestSchema - #27791
Conversation
…anifest through ManifestSchema One helper, readCompiledPluginManifest in @objectstack/runtime, parses an artifact's objectstack.plugin.json through ManifestSchema for both doors. os plugin install refuses an invalid manifest before unpacking as manifest_invalid with the schema's issues (for packaging: 'manifest-deps', the retirement prescription); the boot loader refuses the plugin by name with the schema's message, non-fatal, before importing anything. os plugin build's native-addon refusal now names, for a native package, the files importing it and the chain from the plugin's own source. Claude-Session: https://claude.ai/code/session_016WV4czY8bzmjKmuhj1GP9c Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 3 package(s): 3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 179 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin e11734e0f17b7567db99f90301b26588e1118566 && git checkout e11734e0f17b7567db99f90301b26588e1118566
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin cb074ec3b35d69a1b98fe62ea23d77b8b6051f0c f9521e4fc03120931867cd0361bff810a075d93a && git checkout -B drift-repro cb074ec3b35d69a1b98fe62ea23d77b8b6051f0c && git merge --no-ff f9521e4fc03120931867cd0361bff810a075d93a
node scripts/docs-audit/affected-docs.mjs --json cb074ec3b35d69a1b98fe62ea23d77b8b6051f0c
|
Claude-Session: https://claude.ai/code/session_016WV4czY8bzmjKmuhj1GP9c Co-authored-by: Claude <noreply@anthropic.com>
|
Card thread for #27686 · session_016WV4czY8bzmjKmuhj1GP9c Skills-docs: updated here — Re-run on 75f2611 (README-only delta): |
…oot doors' refusal pins The install door, the boot loader and the os serve boot pin author packaging: 'manifest-deps' on purpose to assert it is refused, as the build door's pin already does; each is excluded by name with its reason. Claude-Session: https://claude.ai/code/session_016WV4czY8bzmjKmuhj1GP9c Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract-review-claim: session_016WV4d1Np3hU7RQnA13YZcL · head a03c314 · 2026-10-29T23:51Z |
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract reviewServed-tier: Face: narrowing — the PR body and the committed changeset both carry ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Implemented-by: VERDICT: FAIL |
…egrity_malformed evidence The changeset now reads `Clause-②: yes (narrowing)`: readCompiledPluginManifest and CompiledPluginManifestRead are new named exports of @objectstack/runtime's root entry. The spec liveness row for `integrity` and the cli platform-checklist item (revision 6) no longer name the deleted `integrity_malformed` refusal: a malformed map is a ManifestSchema issue, refused as manifest_invalid. Claude-Session: https://claude.ai/code/session_016WV4czY8bzmjKmuhj1GP9c Co-authored-by: Claude <noreply@anthropic.com>
|
Card thread for #27686 · session_016WV4czY8bzmjKmuhj1GP9c Corrected declaration (contract review 6511494551, must-fix ①.3) — this line supersedes the body's Clause-②: yes (narrowing) Why: Local on |
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract-review-claim: session_016WV4d1Np3hU7RQnA13YZcL · head f9521e4 · 2026-10-30T00:15Z |
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract reviewServed-tier: Delta review on top of record 6511494551 (FAIL on ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Implemented-by: VERDICT: PASS |
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL 维护者速读
席位意见:ACCEPT。 席位核对Head-sha:
|
Requested by Elon · project thread
Fixes #27686
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Skills-docs: follow-up owed — packages/cli/README.md (its
os plugin installparagraph lists the refusals by name and does not yet namemanifest_invalid; outside this card's declared file surface). No skill underskills/**teaches the compiled-manifest read.维护者速读
os plugin install和启动时加载已安装插件的那一步,现在都用同一个函数把.osplugin里的编译清单按ManifestSchema校验;os plugin build拒收原生插件时会写出是谁把它引进来的。packaging: 'manifest-deps')以前照装不误,到启动时才以import_failed失败,看不到退役处方;现在安装时就被拒,并给出 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:加退役处方。What changes
readCompiledPluginManifest(new,packages/runtime/src/compiled-plugin-manifest.ts, exported from@objectstack/runtime) readsobjectstack.plugin.jsonout of an artifact's files and parses it throughManifestSchema. It answers the parsed manifest, orunreadable(no manifest, not a JSON object) /invalid(the schema's issues, onepath: messageline each, plus the same on one line).os plugin installasks it at step 2, before integrity and before anything is unpacked.invalidis refused asmanifest_invalidwith the issues asdetails; forpackaging: 'manifest-deps'the first line is the retirement prescription.unreadablekeeps itsmanifest_unreadablerefusal. The hand-rolledid/versioncheck (manifest_incomplete) and the hand-rolledintegrityshape check (integrity_malformed) are gone: both are schema issues now, so there is no second copy of either rule. The record'sruntime/grantedPermissionscome from the parsed manifest.installed-plugin-loader.ts, check 4) asks the same helper.invalidrefuses that plugin asmanifest_invalid(new member ofInstalledPluginRefusalCode) with the schema's message, before its entry module is imported; the boot stays non-fatal and the next plugin loads.unreadablekeepsartifact_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.@objectstack/cliand@objectstack/runtimeminor,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 notype→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 gainstype: 'plugin'` (a valid manifest).packages/cli/src/commands/plugin/install.integration.test.ts: a signed, trustedmanifest-depsartifact is refused asmanifest_invalid,detailsopens with the prescription, and nothing exists underplugins/env_local/com.acme.retired; a drifted manifest (noname, an arrayintegrity) is refused naming both issues. Fixture gainsname.packages/cli/test/serve-installed-plugins.integration.test.ts(realos serveboots): a pre-retirement install laid down as an id/version-only install did is refused at boot asmanifest_invalidwith the prescription, notimport_failed, itsinitnever runs, and the boot exits 0. Fixture gainsname.packages/cli/test/plugin-build-bundled-only.test.ts: a transitive native package (src/index.ts → ws-like → fake-native,binding.gyp) is refused namingimported by node_modules/ws-like/lib/buffer-util.jsandreached through src/index.ts → ws-like → fake-native, andws-likeis never named as a native package.json-stdout-purity.e2e.test.tsandschema-migrate.one-shot-family.integration.test.tsfixtures gainname(their artifacts now meetManifestSchema).Ablation (each fix removed on disk, the pins re-run, then restored with
git checkout HEAD --, tree clean):JSON.parse→ 3 failed / 56 passed (the three new loader pins); restored → 59 / 59.JSON.parse, and the build'spackageImporterscall replaced by empty lists → 3 failed / 31 passed (the two install pins + the chain pin); restored → 34 / 34.Acceptance notes
ManifestSchemais consumed as it stands;packages/specis not touched.manifest-packaging-manifest-deps-removedcould 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.os plugin buildproduced from a valid source manifest re-parses unchanged (the boot test'sos plugin buildartifact installs and loads,com.acme.built).Verification
Local readings on
objectstack-ai/objectstackatf6d16b37(baseorigin/main81d00a4e):pnpm --filter @objectstack/runtime typecheck: exit 0.pnpm --filter @objectstack/cli typecheck(tsc + test typecheck): exit 0.packages/runtimefullvitest run: Test Files 482 passed (482), Tests 8767 passed / 19 skipped.packages/clifullvitest run(unit + integration, afterpnpm --filter @objectstack/cli build): Test Files 532 passed / 1 skipped (533), Tests 6637 passed / 9 skipped.packages/clinightly tier,OS_TEST_TIERS=nightly:test/json-stdout-purity.e2e.test.ts77 / 77.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives 61 families; every one run with its exit code recorded before any pipe, pluspnpm lint(exit 0).--ranreconciliation:61 derived famil(ies) accounted for — 59 run, 2 NOT-MEASURED, 0 unrun. 59 exit 0.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.(narrowing)arm): run on the final head, reported in the card's report comment.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4czY8bzmjKmuhj1GP9c