Filed unassigned from the domain:cli lane while implementing #10908. Recording only — #10908 deliberately did not move this, because moving it is a policy decision rather than a repair.
The site
packages/cli/src/commands/serve.ts, the served app's own plugins: [...] config entries. A string entry that is not a bare package name (a relative path, an absolute path, a file:// URL) is imported with the resolution base of serve.ts itself.
For a relative spelling that base is the installed CLI's directory — .../@objectstack/cli/dist/commands/. Measured:
importConfigPlugin('./local-plugin.js')
tried: .../packages/cli/src/commands/local-plugin.js
The served app's root never enters the resolution. So plugins: ['./local-plugin.js'] in an app's objectstack.config.ts can only ever resolve to a file sitting inside the CLI's own install directory, which is not a place any app puts its plugins. Absolute paths and file:// URLs are unaffected — they are base-independent and work correctly today.
Why #10908 did not change it
The obvious repair there was await importFromHost(specifier) for every entry. Measured, that would have moved this base rather than fixed it: createHostImporter passes a non-package specifier through to an import() that lives in @objectstack/types, so the relative spelling would have re-based to @objectstack/types/dist/ — equally not the app's root, and a silent change to a spelling nobody has pinned. #10908 therefore kept the existing base and pinned it, so that the card moved only the case it was filed for (a declared bare package name).
What has to be decided
Nothing measures pull for this spelling today: no content/docs example, no examples/ app, and no test uses a relative string in plugins: [...]. The documented spellings are plugin instances (new AuthPlugin(), createHelloPlugin()) and bare package names (plugins: ['@mycompany/crm'] in content/docs/protocol/kernel/plugin-spec.mdx).
- A. Resolve relative entries against the served app's root. What an author writing a path in their own config would expect, and what comparable tools do. Makes an app-local plugin file work without publishing it as a package. New behaviour on a surface that has none today.
- B. Refuse a relative entry at config load, with a message naming the two spellings that do work (a declared package name, or an absolute /
file:// path). Turns a silent nothing into a loud something without minting a resolution rule. - C. Leave it. The spelling stays inert; nobody is asking for it.
Recommend B unless someone can name a real app that wants A: it costs no new resolution semantics, and it converts a documented-looking spelling that silently cannot work into an authoring-time refusal — which is the direction ADR-0049-style "declared equals enforced" pushes. A is defensible if app-local plugin files are meant to be a supported authoring shape, but that is a product call, not a bug fix.
Either way this wants a maintainer decision before code.
Filed unassigned from the
domain:clilane while implementing #10908. Recording only — #10908 deliberately did not move this, because moving it is a policy decision rather than a repair.The site
packages/cli/src/commands/serve.ts, the served app's ownplugins: [...]config entries. A string entry that is not a bare package name (a relative path, an absolute path, afile://URL) is imported with the resolution base ofserve.tsitself.For a relative spelling that base is the installed CLI's directory —
.../@objectstack/cli/dist/commands/. Measured:The served app's root never enters the resolution. So
plugins: ['./local-plugin.js']in an app'sobjectstack.config.tscan only ever resolve to a file sitting inside the CLI's own install directory, which is not a place any app puts its plugins. Absolute paths andfile://URLs are unaffected — they are base-independent and work correctly today.Why #10908 did not change it
The obvious repair there was
await importFromHost(specifier)for every entry. Measured, that would have moved this base rather than fixed it:createHostImporterpasses a non-package specifier through to animport()that lives in@objectstack/types, so the relative spelling would have re-based to@objectstack/types/dist/— equally not the app's root, and a silent change to a spelling nobody has pinned. #10908 therefore kept the existing base and pinned it, so that the card moved only the case it was filed for (a declared bare package name).What has to be decided
Nothing measures pull for this spelling today: no
content/docsexample, noexamples/app, and no test uses a relative string inplugins: [...]. The documented spellings are plugin instances (new AuthPlugin(),createHelloPlugin()) and bare package names (plugins: ['@mycompany/crm']incontent/docs/protocol/kernel/plugin-spec.mdx).file://path). Turns a silent nothing into a loud something without minting a resolution rule.Recommend B unless someone can name a real app that wants A: it costs no new resolution semantics, and it converts a documented-looking spelling that silently cannot work into an authoring-time refusal — which is the direction ADR-0049-style "declared equals enforced" pushes. A is defensible if app-local plugin files are meant to be a supported authoring shape, but that is a product call, not a bug fix.
Either way this wants a maintainer decision before code.