Observation-class finding from #8343. No measured user impact today — filed so it is not rediscovered as a mystery later.
Kernel.use() stores plugins in a Map keyed by plugin.name, so registering a second plugin with the same name replaces the first entry rather than erroring or de-duplicating.
The marketplace wiring block in packages/cli/src/commands/serve.ts mounts MarketplaceProxyPlugin, MarketplaceInstallLocalPlugin, the cloud-connection surface and RuntimeConfigPlugin whenever resolveCloudUrl() is truthy and the kernel is not a runtime host kernel. It does not check whether the loaded host config already wired any of them. A host that mounts its own therefore has it replaced by the CLI's instance, constructed with the CLI's own arguments.
cloud's apps/objectos-ee/objectstack.config.ts is exactly that host: in single-environment mode with a real OS_CLOUD_URL it wires MarketplaceProxyPlugin, MarketplaceInstallLocalPlugin and createCloudConnectionPlugin itself, from the same classes (@objectstack/objectos-runtime re-exports them from @objectstack/cloud-connection, single source since ADR-0008 Phase 2). Today both sides construct them with the same resolveCloudUrl() value, so the replacement is behaviourally invisible. It stops being invisible the moment a host passes anything the CLI does not — a distinct control-plane URL, a custom storageDir, cache tuning.
Note the host-kernel guard does not cover this case: it detects ObjectOSEnvironmentPlugin, which the EE single-environment branch never constructs (only the OS_MULTI_TENANT branch does, via createObjectOSStack).
#8343 added exactly this presence check to the offline arm it introduced, using the existing Serve.providesCapability idiom, because there the replacement would be a genuine downgrade (an off-pinned instance overwriting a host's URL-configured one). The cloud-connected arm was deliberately left as-is: making the host win there is a real behaviour change for connected runtimes and wanted its own card.
Suggested direction
Apply the same identity check to the cloud arm, so a host config that wires its own marketplace plugins keeps them and the CLI auto-wiring stays a fallback for hosts that wire nothing. Alternatively, make Kernel.use() refuse or warn on a duplicate name instead of silently replacing — a broader fix with a much larger blast radius.
Backlink: #8343.
Generated by Claude Code
Observation-class finding from #8343. No measured user impact today — filed so it is not rediscovered as a mystery later.
Kernel.use()stores plugins in aMapkeyed byplugin.name, so registering a second plugin with the same name replaces the first entry rather than erroring or de-duplicating.The marketplace wiring block in
packages/cli/src/commands/serve.tsmountsMarketplaceProxyPlugin,MarketplaceInstallLocalPlugin, the cloud-connection surface andRuntimeConfigPluginwheneverresolveCloudUrl()is truthy and the kernel is not a runtime host kernel. It does not check whether the loaded host config already wired any of them. A host that mounts its own therefore has it replaced by the CLI's instance, constructed with the CLI's own arguments.cloud'sapps/objectos-ee/objectstack.config.tsis exactly that host: in single-environment mode with a realOS_CLOUD_URLit wiresMarketplaceProxyPlugin,MarketplaceInstallLocalPluginandcreateCloudConnectionPluginitself, from the same classes (@objectstack/objectos-runtimere-exports them from@objectstack/cloud-connection, single source since ADR-0008 Phase 2). Today both sides construct them with the sameresolveCloudUrl()value, so the replacement is behaviourally invisible. It stops being invisible the moment a host passes anything the CLI does not — a distinct control-plane URL, a customstorageDir, cache tuning.Note the host-kernel guard does not cover this case: it detects
ObjectOSEnvironmentPlugin, which the EE single-environment branch never constructs (only theOS_MULTI_TENANTbranch does, viacreateObjectOSStack).#8343 added exactly this presence check to the offline arm it introduced, using the existing
Serve.providesCapabilityidiom, because there the replacement would be a genuine downgrade (anoff-pinned instance overwriting a host's URL-configured one). The cloud-connected arm was deliberately left as-is: making the host win there is a real behaviour change for connected runtimes and wanted its own card.Suggested direction
Apply the same identity check to the cloud arm, so a host config that wires its own marketplace plugins keeps them and the CLI auto-wiring stays a fallback for hosts that wire nothing. Alternatively, make
Kernel.use()refuse or warn on a duplicate name instead of silently replacing — a broader fix with a much larger blast radius.Backlink: #8343.
Generated by Claude Code