From 19236a4bddd5fcba1f207487f6429fcc09757519 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Thu, 4 Jun 2026 14:33:14 -0700 Subject: [PATCH] Document bundled-plugin registration drift learning Capture the 4-list registration requirement for bundled plugins (Settings catalog, dashboard fallback set, CLI auto-install list, tsup staging) in docs/solutions/, and add the Bundled Plugin concept to CONCEPTS.md. Co-Authored-By: Claude Opus 4.8 (1M context) --- CONCEPTS.md | 8 ++ .../bundled-plugin-registration-drift.md | 79 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 docs/solutions/integration-issues/bundled-plugin-registration-drift.md diff --git a/CONCEPTS.md b/CONCEPTS.md index 8f4df9fce6..0825bb7ffb 100644 --- a/CONCEPTS.md +++ b/CONCEPTS.md @@ -134,6 +134,14 @@ The user's mid-stage feedback channel: free-text guidance attached to an answer, ### Rehydration Re-establishing a live agent handle for a paused CE Session by replaying its recorded conversation against the model. Replay is side-effect-suppressed: it reconstructs the agent's context without re-emitting events, re-streaming Live activity, or re-writing artifacts. +## Plugins + +### Bundled Plugin +A plugin that ships inside the Fusion distribution itself rather than being installed from a user-supplied path — it appears under Settings → Built-in Plugins and can be auto-installed at startup. +*Avoid:* built-in plugin (as a distinct concept; the Settings label uses "Built-in" for the same thing) + +A Bundled Plugin must be registered in several independently maintained surfaces — the Settings catalog, the dashboard server's bundled-id fallback set, the CLI's startup auto-install list, and the build step that stages a loadable copy into the distribution. The surfaces do not cross-check each other: a plugin registered in some but not all appears installable yet fails to install or load, so adding one means mirroring an existing bundled plugin across every surface. + ## Flagged ambiguities - "Merging" a shared-branch-group Task had been used for both member integration and group promotion — these are distinct steps with independent gating and must not be conflated. diff --git a/docs/solutions/integration-issues/bundled-plugin-registration-drift.md b/docs/solutions/integration-issues/bundled-plugin-registration-drift.md new file mode 100644 index 0000000000..b9b485a893 --- /dev/null +++ b/docs/solutions/integration-issues/bundled-plugin-registration-drift.md @@ -0,0 +1,79 @@ +--- +title: Bundled plugins must be registered in 4 independent places — they drift +date: 2026-06-04 +category: integration-issues +module: plugins +problem_type: integration_issue +component: tooling +symptoms: + - "Installing a built-in plugin from Settings → Built-in Plugins fails with \"Plugin manifest not found. Looked for manifest.json in: ...\"" + - "Plugin shows in the Settings UI but the install POST returns 404" + - "Packaged (npm/binary) installs report missing-bundle for a plugin that works in dev" +root_cause: incomplete_setup +resolution_type: code_fix +severity: medium +tags: [plugins, bundled-plugins, settings, install, tsup, registration-drift] +--- + +# Bundled plugins must be registered in 4 independent places — they drift + +## Problem + +Adding a bundled (built-in) plugin to Fusion requires registration in **four independently maintained lists** with no cross-check. `fusion-plugin-compound-engineering` was added to only 2 of 4, so installing it from Settings → Built-in Plugins failed with "Plugin manifest not found" (fixed in PR #1423). + +The four registration points: + +1. **Dashboard UI** — `BUILTIN_PLUGINS` in `packages/dashboard/app/components/PluginManager.tsx` (makes the card appear in Settings) +2. **Dashboard server** — `BUNDLED_PLUGIN_IDS` in `packages/dashboard/src/routes.ts` (lets the install route fall back to the bundled copy when the relative `./plugins/...` path misses the server cwd) +3. **CLI startup** — `BUNDLED_PLUGIN_IDS` in `packages/cli/src/plugins/bundled-plugin-install.ts` (auto-install/upgrade of bundled plugins) +4. **Build staging** — `packages/cli/tsup.config.ts` (`bundlePluginEntry` or a copy block staging the plugin into `dist/plugins//` so packaged installs have a copy at all) + +A plugin with a dashboard view additionally needs client-side view registration in `packages/dashboard/app/plugins/registerBundledPluginViews.ts`. + +## Symptoms + +- Settings shows the plugin card, but clicking install errors with `Plugin manifest not found. Looked for manifest.json in: /plugins/` — the cwd-relative path missed and the bundled fallback was skipped because the id wasn't in routes.ts's `BUNDLED_PLUGIN_IDS`. +- A sibling plugin added in the same commit (roadmap) installs fine — it was in all four lists. +- In packaged installs, `ensureBundledPluginInstalled` logs/returns `missing-bundle` because tsup never staged the plugin into `dist/plugins/`. + +## What Didn't Work + +- Assuming the UI list + CLI list were sufficient — the dashboard server keeps its **own** copy of the bundled-id set, and the install route's fallback silently returns null for unknown ids. +- The existing bundled-fallback route tests appeared to cover this, but their mocks let cwd resolution succeed (mock matched any path containing the plugin id), so the fallback branch was never actually exercised. + +## Solution + +Register the plugin in all four places. For the missing two: + +```ts +// packages/dashboard/src/routes.ts +const BUNDLED_PLUGIN_IDS = new Set([ + // ... + "fusion-plugin-cli-printing-press", + "fusion-plugin-compound-engineering", +]); +``` + +```ts +// packages/cli/tsup.config.ts (onSuccess) +await bundlePluginEntry({ + pluginId: "fusion-plugin-compound-engineering", + srcDir: compoundEngineeringPluginSrc, + destDir: compoundEngineeringPluginDest, +}); +``` + +## Why This Works + +The Settings card sends a relative `./plugins/` path. The server resolves it against `process.cwd()` — normally the user's project dir, not the Fusion repo — so it 404s and falls back to `extractBundledPluginId()`, which only recognizes ids in routes.ts's `BUNDLED_PLUGIN_IDS`. Adding the id makes the fallback resolve the staged bundled copy; the tsup staging block guarantees that copy exists in packaged installs. + +## Prevention + +- **When adding a bundled plugin, grep for an existing one** (e.g. `rg -l "fusion-plugin-roadmap" packages/` ) and mirror every hit — that surfaces all four lists plus view registration. +- Route tests must force the fallback: mock fs so the cwd-relative path **misses** and only `dist/plugins/` exists (see "installs bundled compound engineering plugin when relative path misses cwd" in `packages/dashboard/src/__tests__/plugin-routes.test.ts`). A mock that matches any path containing the plugin id tests nothing. +- Consider a future consistency test asserting every `BUILTIN_PLUGINS` UI entry with a `path` is present in both server-side `BUNDLED_PLUGIN_IDS` sets. + +## Related Issues + +- PR #1423 — the fix +- Commit `ff0750cd1` — added CE/Roadmap to the UI list (2 of 4 registrations)