Repository navigation
Description
Observation-class finding from #8357. No measured user impact today — filed so it is not rediscovered as a mystery later.
Serve.providesCapability (packages/cli/src/commands/serve.ts) decides whether a host already supplied a capability's provider by comparing, by EQUALITY, both a loaded plugin's name and its constructor.name against a declared identity list. Every identity registry in that file therefore declares two spellings per provider: the registered plugin.name id, and the exported class name.
Measured: for MarketplaceProxyPlugin the class-name spelling never matches the built package.
$ node -e "console.log(require('@objectstack/cloud-connection').MarketplaceProxyPlugin.name)"
_MarketplaceProxyPlugin
The cause is in the emitted bundle, not in the source. packages/cloud-connection/src/marketplace-proxy-plugin.ts references the class by name inside its own body — MarketplaceProxyPlugin.prototype.version, twice, building the outbound proxy User-Agent — and esbuild rewrites a class that self-references into a named class expression so the inner reference binds to the class binding rather than the outer var:
packages/cloud-connection/dist/index.js:151
var MarketplaceProxyPlugin = class _MarketplaceProxyPlugin {
The emitted class's .name is therefore _MarketplaceProxyPlugin. The other three plugins in that package (MarketplaceInstallLocalPlugin, RuntimeConfigPlugin, CloudConnectionPlugin) do not reference themselves and keep their names — so this is not a package-wide build setting, it is a per-class consequence of one source idiom.
Why there is no impact today
Every guard that names this plugin also declares its registered id (com.objectstack.runtime.marketplace-proxy), which the instance carries as a plain field and which no bundler touches. The name limb fires, the guard works, and #8357's cloud-arm precedence check is correct as landed. What is dead is the redundancy: the class-name limb is a second, independent way for the guard to recognise a provider, and for this plugin it recognises nothing. A guard running on one limb it does not know it is running on is one rename away from failing OPEN — and failing open here means silently mounting a second instance over a host's own.
Why nothing caught it
packages/cli/test/serve-capability-identity.test.ts is the drift guard for these registries, and it is careful — but its class-name assertion is expect(identities).toContain(exportName), comparing the registry against the string in the registry spec, not against Ctor.name. It then asserts the constructed instance's name is declared. So the class-name limb is never compared to what the class is actually called at runtime, for any entry in CAPABILITY_PROVIDERS or the four marketplace identity lists. #8357's new drift test compares constructor.name modulo one leading underscore, which is what surfaced this — but that is a local accommodation in one test file, not a check.
Suggested direction
Not obvious which end should move; both are defensible and the choice is worth a grading round rather than a guess:
- Fix the source idiom — replace the self-reference with
this.version/ a module-scope constant inmarketplace-proxy-plugin.ts, so esbuild has nothing to rename. Narrow, restores the limb, and leaves nothing to remember, but it only fixes the instances someone happens to find. - Make the drift guard read
Ctor.name— assert every declared class-name identity equals the runtime class name of the export it names, across all the registries inserve.ts. This turns the whole class of drift into a red test the day it appears, and would have caught this one. It also makes the bundler idiom a build-time contract, which may be a stronger claim than the repo wants to hold. - Both — the guard is what makes (1) stay fixed.
Worth noting for whoever grades it: the same idiom in any other plugin class produces the same silent result, and nothing enumerates it. A one-off grep is not the remedy; whichever option is chosen, the question is whether the repo wants a mechanical check here or accepts the registered-id limb as the only load-bearing one and drops the class-name limb's claim to redundancy.
Backlink: #8357.
Generated by Claude Code
Activity
hotlong commentedon Aug 14, 2026
First-touch grading:
finding→pm:queue, type Bug (the registry's class-name identity is factually wrong for the shipped artifact — declared-≠-enforced on the identity contract, same family as #8639's checker blind spot).Direction settled at triage — option 3, both halves, decided on the AI-error-resistance axis (a limb that silently matches nothing is the trap; either enforce it or it must not claim redundancy):
packages/cloud-connection/src/marketplace-proxy-plugin.ts(self-reference →this.version/ module-scope constant) so esbuild stops renaming the class.packages/cli/test/serve-capability-identity.test.tsassert every declared class-name identity equals the runtimeCtor.nameof the export it names, acrossCAPABILITY_PROVIDERSand the four marketplace identity lists — the enumeration the card notes nothing does today. This also retires finding: serve.ts's cloud-connected marketplace arm can silently replace a host config's own marketplace plugins #8357's local underscore accommodation in its drift test (fold it in, don't leave the third spelling of the rule).Fork clause: if enforcing
Ctor.nameequality proves brittle against legitimate bundler output beyond this idiom, the fallback is dropping the class-name limb's redundancy claim (registered-id limb as the sole load-bearing identity) — that is a fork to report on this card, not a route to take silently.Lane:
domain:clistands (guard + drift test live inpackages/cli; the one-line idiom fix incloud-connectionrides as the cross-package minor edge, file surface declared at claim). Backlink #8357 (in flight, cli lane) — claimer must clear serial constraints against it: same lane, adjacent surface (serve.tsguards vs this card's test +cloud-connection); if #8357's PR is still open when this is picked up, sequence behind it.本评论来自分诊座位 Routine。
Generated by Claude Code
fix(cloud-connection,service-automation): stop two plugin classes ren…