Repository navigation
fix(runtime,cli): a newcomer's first boot is quiet — the empty stub declares its protocol range, and a scaffold's os dev composes one MetadataPlugin - #30333
Conversation
…l range The empty-boot stub (`com.objectstack.empty`) had no `engines.protocol`, so the ADR-0087 handshake warned about it on the first `os start` in an empty directory. It now declares `^<PROTOCOL_MAJOR>`, and a stub an older CLI wrote (no range, or another major) is refreshed in place. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CZgQTno2oPVD2GLd2n
… by name The dev door checked only `constructor.name === 'MetadataPlugin'`, but the published @objectstack/metadata bundle names the class `_MetadataPlugin`. On an installed CLI the check missed the standalone stack's MetadataPlugin and composed a second one, so `os dev` on a fresh `os init -t app` scaffold warned "Plugin superseded". Both serve doors now ask one `hasMetadataPlugin` predicate (plugin name, then class name). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CZgQTno2oPVD2GLd2n
… path Once `<home>/dist/objectstack.json` exists, `os start` resolves it as an artifact rung and passes it as `artifactPath`, so the empty-boot branch never runs again and a stub an older CLI wrote kept warning. The stub at the home location is now refreshed on that path too; an app artifact at the same path, or a stub-shaped file anywhere else, is left alone. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CZgQTno2oPVD2GLd2n
…rity census serve.ts now reads `hasMetadataPlugin` from stack-collections.ts; the migrate boot composes no MetadataPlugin of its own (the standalone data stack always holds one), so it is a SERVE_ONLY entry with that reason. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CZgQTno2oPVD2GLd2n
📓 Docs Drift CheckThis PR changes 2 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 51 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 13b5a416897db0a56cad182759a29cb0c05dfc64 && git checkout 13b5a416897db0a56cad182759a29cb0c05dfc64
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c1240a38ac2c23c8d526e476a20edeabbbefd6f6 2041627b97d9d3f43dac4219676673ac5c1ce2b3 && git checkout -B drift-repro c1240a38ac2c23c8d526e476a20edeabbbefd6f6 && git merge --no-ff 2041627b97d9d3f43dac4219676673ac5c1ce2b3
node scripts/docs-audit/affected-docs.mjs --json c1240a38ac2c23c8d526e476a20edeabbbefd6f6
|
|
domain:server#3 · os-bill · session_01Utp6CbPxYq1wQmUNWvMZkC 席位核对ACCEPT · head
|
Requested by Bill · project thread
domain:server#3 · os-bill · session_01Utp6CZgQTno2oPVD2GLd2n
Fixes #29937
Fixes #29936
Clause-②: no — boot diagnostics only; nothing accepted or published changes
Metadata-protocol: none
维护者速读
com.objectstack.empty现在声明engines.protocol: ^18(写它的运行时自己的大版本);旧 CLI 写过的存根(无范围、或别的大版本)在下次os start时就地补上,只改这一个键,且只改 OS_HOME 下仍是存根的文件。②os serve/os dev的 dev artifact door 与 host-config door 共用一个hasMetadataPlugin判定(先认插件名com.objectstack.metadata,再认类名),dev door 不再在已有MetadataPlugin的栈上再加一个。npm i -g @objectstack/cli && os start的首启诊断报「com.objectstack.empty没声明 engines.protocol」,新人既不能修也找不到这个包;os init -t app脚手架上os dev每次启动都报「Plugin superseded: 'com.objectstack.metadata'」,怪用户配错了一个他没写过的插件。后者的根因:发布的@objectstack/metadata打包产物写成var MetadataPlugin = class _MetadataPlugin,constructor.name是_MetadataPlugin,dev door 只比类名,所以认不出 standalone stack 已组好的那个;源码跑的测试看不到这个改名。dist/objectstack.json且manifest.id仍是com.objectstack.empty时发生,别处的同形文件、非 JSON 文件一律不碰(有 pin)。dev door 的判定只比原来多认一种已有插件的写法,host config 没有自己的MetadataPlugin时照旧组 dev door。两个包各一条 patch changeset。回滚即撤 PR。packages/runtime/src/default-host.ts的ensureEmptyBootStub与createDefaultHostConfig里按路径传入时的刷新分支;packages/cli/src/utils/stack-collections.ts的hasMetadataPlugin;packages/cli/src/commands/serve.ts两处门控。os start:首启诊断里不再出现declares no engines.protocol range,~/.objectstack/dist/objectstack.json带"engines": { "protocol": "^18" }。What changes
#29937 — the empty kernel's stub declares its range (
@objectstack/runtime, commits 4a5bb32 and 8e3cead)ensureEmptyBootStub(stubPath)(new, indefault-host.ts; not re-exported from the package index) owns the stub. Absent: it is written withmanifest.engines.protocol=^PROTOCOL_MAJOR(from@objectstack/spec/kernel), the major of the runtime that writes it, so it is compatible by construction. Present and still the stub (manifest.idiscom.objectstack.empty) with no range or another major's: onlymanifest.engines.protocolis rewritten. Present and not the stub, or not JSON: left alone; the loader reports it.^18by this CLI would otherwise be refused asOS_PROTOCOL_INCOMPATIBLEthe day the runtime moves to 19, failing the one boot that has nothing in it.createDefaultHostConfigruns only on the FIRSTos start. From the second on, theos startsupervisor resolvesHOME_DIR/.objectstack/dist/objectstack.jsonas an artifact rung (resolveArtifactBootSource, runghome-dist) and hands it down asartifactPath, so a stub an older CLI wrote was never reached by the first commit and kept warning (measured, log below). The second commit refreshes the stub on that path too, keyed on the resolved path being exactly the home stub path; a stub-shaped file named anywhere else is left alone.#29936 — one
MetadataPluginon a scaffold'sos dev(@objectstack/cli, commit 028a043)hasMetadataPlugin(plugins)(new,utils/stack-collections.ts, besidehasObjectQLPlugin/hasStorageDriverPlugin): a plugin namedcom.objectstack.metadata, or a class namedMetadataPlugin.serve.ts: the dev artifact door's gate was!plugins.some((p) => p?.constructor?.name === 'MetadataPlugin'); the host-config door's was the name-or-class form. Both now read!hasMetadataPlugin(plugins).createStandaloneStackis untouched, as the dispatch asked.@objectstack/metadata@18.3.0dist/index.jsand on this branch's own build ofpackages/metadata/dist/index.jsandnode.js, one hit each) declaresvar MetadataPlugin = class _MetadataPlugin {. The host-config door already checked the name, so it was immune; the dev door was not.Tests
packages/runtime/src/default-host-empty-stub.test.ts(new, 9 cases): a fresh stub passes the handshake (checkProtocolCompat(...).status === 'ok'); a legacy stub (exactly the 18.3.0 bytes' shape) is refreshed with everything else kept; another major's range is re-stamped; a current stub is left byte for byte; a non-stub artifact and a non-JSON file are untouched; throughcreateDefaultHostConfig: the legacy stub named asartifactPathat the home location is refreshed, the empty boot writes the range, and a stub-shaped file named elsewhere is untouched.packages/cli/src/utils/stack-collections.test.ts: threehasMetadataPlugincases, one built on the bundle's shape (class _MetadataPlugin { name = 'com.objectstack.metadata' }).serve-host-config-security-registrar.pin.test.ts(the dev door's gate string) andserve-host-config-metadata-door.pin.test.ts(both doors ask!hasMetadataPlugin(plugins), count 2, and no class-name-only spelling is left inserve.ts).Verification
All readings on head 2041627 (gates: the 61-family run on 8e3cead, then the 17 families that read the last commit's file re-run on 2041627; the last commit adds one
SERVE_ONLYentry to a cli test).Real boot, before/after (#29937: os start, empty directory, empty HOME)
Built CLI of this branch (
node packages/cli/bin/run.js start,HOMEa fresh temp dir).default-host.tsrestored from a26abbc, runtime rebuilt;ensureEmptyBootStubcount in dist 0):Artifact: none (empty kernel …), thenℹ Boot diagnostics — 1 informational: WARN [AppPlugin] [protocol] package 'com.objectstack.empty' declares no engines.protocol range; loading under protocol 18.0.0 without a compatibility check (ADR-0087).Server is ready, noBoot diagnosticsblock; the stub on disk carries"engines": { "protocol": "^18" }.Artifact: HOME_DIR/.objectstack/dist/objectstack.json(thehome-distrung), the same WARN, stub unchanged. This is what commit 8e3cead closes.Boot diagnostics; the stub now carries"engines": { "protocol": "^18" }.git diff HEADempty, runtime rebuilt from HEAD (distensureEmptyBootStub(count 3).Real boot, before/after (#29936: os dev on an os init my-app -t app scaffold)
serve.tsrestored from a26abbc, cli rebuilt):ℹ Boot diagnostics — 1 informational: WARN Plugin superseded: 'com.objectstack.metadata' — the later registration (v1.0.0) REPLACED the earlier one (v1.0.0). …Server is ready,supersededcount 0, noBoot diagnosticsblock;Plugins: 41 loadedlistscom.objectstack.metadataonce.git diff HEADempty.Ablations (scripts/ablation-replace.mjs; each restored, blob == HEAD, git diff HEAD empty)
hasMetadataPluginname clause dropped (return ctor === 'MetadataPlugin';):stack-collections.test.ts1 failed / 21 passed —finds the plugin by its name when the bundle renamed the class.engines: { protocol: range },removed:default-host-empty-stub.test.ts3 failed / 6 passed (fresh stub, current-stub-left-alone, empty boot writes the range).refreshes a legacy stub handed in as artifactPath at the home location.Tests and typecheck
pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2: Test Files 545 passed (545), Tests 9840 passed, 19 skipped (9859); lock VERDICT command-exit 0.pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2on 8e3cead: 1 failed / 5429 passed —boot-preparation-parity.test.tsasked forhasMetadataPluginto be listed; fixed in 2041627 (SERVE_ONLYentry with its reason), that file then 37/37. The cliintegrationtier is declared to CI.pnpm --filter @objectstack/runtime typecheck,pnpm --filter @objectstack/cli typecheck: VERDICT command-exit 0 both.Derived gates (dispatch-gates --commands, 61 families)
check:dual-build-cjs-loads,check:i18n-coverage; left to CI.dispatch-gates --ran: 61 derived, 59 run, 2 NOT-MEASURED, 0 UNRUN.Acceptance notes
os startwith no project, the docs' one-line Community quickstart, warns that the platform's own empty-kernel stubcom.objectstack.emptydeclares no engines.protocol range #29937 has two (4a5bb32, 8e3cead): the second is the named-path refresh the measurement found after the first was pushed. The branch isclaude/project-thread-*, notclaude/issue-*, so AGENTS.md §3 forbids rewriting it; they stay two commits.os devon anos init -t appscaffold warns "Plugin superseded: 'com.objectstack.metadata'" on every boot, because the platform registers its own metadata plugin twice #29936 likewise gains 2041627, the boot-parity census entry its unit run asked for.os devon anos init -t appscaffold warns "Plugin superseded: 'com.objectstack.metadata'" on every boot, because the platform registers its own metadata plugin twice #29936 ran this branch's CLI against anos init my-app -t appscaffold whose@objectstack/spec,runtimeandobjectqlwere linked to this worktree's packages, so both copies of the boot read this branch's builds.no-rangewarning itself, andcreateStandaloneStack.Generated by Claude Code