Repository navigation
fix(cli)!: os plugin publish parses the compiled manifest through ManifestSchema before any upload - #27910
Conversation
…ifestSchema before any upload `os plugin publish` and `readOspluginManifest` read the artifact's objectstack.plugin.json with a bare JSON.parse, so a stale artifact (for example one carrying the retired `packaging: 'manifest-deps'`) reached the registry and failed only at its consumer's install. Both now ask `readCompiledPluginManifest`, the helper `os plugin install` and the boot loader use, and publish refuses an invalid manifest with the schema's issues before any request is sent. The uploaded plugin_manifest stays the artifact's own manifest, without the defaults the parse stamps. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyHFdayr8QnKE8nszT
📓 Docs Drift CheckThis PR changes 1 package(s): 2 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 — 35 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 005bd7b3809786f64fdd215561827c1790b0d532 && git checkout 005bd7b3809786f64fdd215561827c1790b0d532
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d770b1505e5b172c5a0cea2c22878067da2966b2 bd6d77e29bc0479703070e9407f5fca7795349c6 && git checkout -B drift-repro d770b1505e5b172c5a0cea2c22878067da2966b2 && git merge --no-ff bd6d77e29bc0479703070e9407f5fca7795349c6
node scripts/docs-audit/affected-docs.mjs --json d770b1505e5b172c5a0cea2c22878067da2966b2
|
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract-review-claim: session_016WV4d1Np3hU7RQnA13YZcL · head bd6d77e · 2026-10-30T03:27Z |
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract reviewServed-tier: Face: Check-runs on the head at 2026-10-30T03:37Z: every completed, non-skipped run is ① Derived judgments
② Semver level
③ Boundary flagsDev flag (1) — Dev flag (2) — No ④ Design judgment
Verdict on the narrowing itself, against the published spec on Implemented-by: VERDICT: PASS |
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL 维护者速读
席位意见:ACCEPT。 席位核对Head-sha:
|
Requested by Elon · project thread
Fixes #27810
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Skills-docs: none — no skill under
skills/**and no page undercontent/docsteaches whatos plugin publishchecks in the compiled manifest (skills/objectstack-platform/references/operations.mdandpackages/cli/README.mdlist the command as "upload / publish a signed .osplugin", which stays true); the QA ledger row that did name the old refusal is re-pointed here.维护者速读
os plugin publish现在用安装与启动已在用的同一个函数(readCompiledPluginManifest),按ManifestSchema校验.osplugin里的编译清单,校验不过就在发任何网络请求之前拒绝;CLI 内部的readOspluginManifest也改走这个函数。JSON.parse,旧版本打出的包(比如带已退役的packaging: 'manifest-deps')能照样传到市场,要到用户安装时才被拒;现在在发布者自己的终端就被拒,并给出 schema 的处方。name/type、多余键、畸形integrity等);合法的包照常发布,上传的plugin_manifest仍是包里原样的清单(不带解析时补上的默认值)。回滚即回退本 PR。packages/cli/src/commands/plugin/publish.ts第 2 步(校验与原样上传两段)→packages/cli/src/utils/osplugin.ts的readOspluginManifest→packages/cli/test/plugin-publish.test.ts新增的拒绝与原样上传钉子。pnpm --filter @objectstack/cli exec vitest run test/plugin-publish.test.ts -t manifest-deps,预期两条通过:发布被拒、零次请求、输出含packaging: `packaging: 'manifest-deps'` was removed与Delete the key。What changes
os plugin publish, step 2. The compiled manifest is read withreadCompiledPluginManifestfrom@objectstack/runtime(the helper PR fix(cli)!: os plugin install and the boot loader parse the compiled manifest through ManifestSchema #27791 gaveos plugin installand the boot loader), right after the archive is read and before the integrity preflight, the signature read, the auth lookup and both POSTs.unreadablekeeps the existingCannot read manifest from artifact: …refusal.invalidprintsThe artifact's objectstack.plugin.json is not a valid plugin manifest, so nothing was published:, one• path: messageline per schema issue (formanifest-deps, the retirement prescription), and the rebuild / re-sign remedy, then exits 1.integritycheck is gone: the map's shape is a schema issue.Artifact manifest is missing id or version.stays, now reachable only from an empty--manifest-id.id,version,name,runtime,description,integrity) comes from the parsed manifest.plugin_manifestis the artifact's own manifest, unchanged. Flag for the reviewer.ManifestSchemastamps two defaults at parse (scope: 'project',defaultDatasource: 'default'), so uploading the parsed value would make the declared manifest differ from the one inside the signed bytes on every valid publish. After the helper answersok, publish decodes the same entry once more for the payload only. That decode reads no rule: the verdict and every read field are the helper's. The pinuploads the manifest the artifact carries, as it carries itreds ifplugin_manifestbecomes the parsed value (measured, below). The helper's module is untouched.readOspluginManifest(internal to the CLI, not in anyexportsentry; its only caller isplugin-publish.test.ts, so no inspect/diagnose path depends on a lenient read) parses through the same helper, throws the helper's message, and answers the parsed manifest. It is nowasyncand imports@objectstack/runtimeon call: everyos plugincommand loadsosplugin.ts, and the runtime's root entry measured ~1.2 s to import against ~0.3 s for@objectstack/core, whichos plugin buildandos plugin signwould otherwise pay for a read they never make.publish.tsimports the helper statically, asinstall.tsdoes.manifest-packaging-retirement.test.tsgains oneEXCLUDEDentry,packages/cli/test/plugin-publish.test.ts("the publish door's own pin authors it to assertos plugin publishrefuses it before any upload"), the same shape cli:os plugin installreads onlyid/versionof the compiled manifest, so a stale artifact (e.g.packaging: manifest-deps) installs and fails at boot asimport_failedinstead of meeting the prescription #27686 used (claim amended by the seat first).docs/qa/platform-checklist/areas/cli.json, itemcli.plugin-manifest-build-contract: the compiled-manifest clause and its verify step now name the schema refusal instead ofArtifact manifest is missing id or version., thepublish.ts#versionsource row is re-worded, revision 6 → 7 with a history entry (claim amended by the seat first).@objectstack/climinor,fix(cli)!, BREAKING banner,adr-0087: not-required (no-migration-prescription), both declaration lines.Tests
packages/cli/test/plugin-publish.test.ts, five new pins and two updated:readOspluginManifestthrows themanifest-depsprescription (packaging: `packaging: 'manifest-deps'` was removed,Delete the key).readOspluginManifestthrows a drifted manifest's issues by name (name:,integrity:).manifest-depsartifact with zerofetchcalls, the prescription on a•line, and theos plugin buildremedy.name,integrityan array) with zerofetchcalls, naming both issues.plugin_manifestdeep-equal to the artifact's manifest, with noscope/defaultDatasource.Ablation, on disk: restoring
publish.tsandosplugin.tsfrommainturns the four refusal pins red (Tests 4 failed | 8 passed); settingplugin_manifest: manifest(the parsed value) turns the verbatim pin red (1 failed | 11 passed). Both restored; the file then passes 12/12.Acceptance notes
fetchcalls (0).publish.tsorosplugin.ts; both askreadCompiledPluginManifest.Verification
Head
bd6d77e2. Gate readings (87 derived families,pnpm lint, typecheck, the CLI unit layer, the dogfood battery) are in theos-dev-reportcomment on #27810, read from their logs;dispatch-gates --ranreconciled 87 derived / 87 run / 0 NOT-MEASURED, every exit 0.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4cyHFdayr8QnKE8nszT