c53f699714
- go plugin backends ship as release tarballs (per-platform binaries, checksums) that kandev installs from url/upload or filesystem sideload+sync, then spawns and supervises via hashicorp/go-plugin (unix socket / loopback tcp+automtls) - strict typed grpc contract (kandev.plugin.v1): DeliverEvent/InvokeTool/HandleWebhook plus a capability-gated host service (state, secrets, emit); author sdk in pkg/pluginsdk; packaging cli in cmd/plugin-pack - native frontend bundles: window.registerKandevPlugin + registry/host loader with dynamic routes, nav items, slots and ws handlers; plugins settings page with install dialog, sync button and enable/disable/uninstall - gated behind the plugins feature flag (prod off, dev/e2e on); covered by unit and integration tests plus playwright e2e against a real packaged fixture plugin Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3.0 KiB
3.0 KiB
id, title, status, wave, depends_on, plan
| id | title | status | wave | depends_on | plan | |
|---|---|---|---|---|---|---|
| task-19 | Frontend dynamic integration — routes/nav/slots/ws + PluginSlot | done | B |
|
docs/plans/plugins/plan.md |
Frontend dynamic integration
Title
Wire the plugin registry into the live app: dynamic SPA routes, nav items, slot host component, WS handler bridge, and boot the plugin host on app start.
Inputs
PLUGIN-API.md"Integration points the app must add".- task-18 exports:
PluginRegistrysingleton +usePluginRegistry(),installPluginGlobal,loadPlugins,buildHostApi. - Seams:
apps/web/src/spa-routes.tsx(static route resolver — add registry fallthrough before not-found),apps/web/src/settings-routes.tsx(/settings/plugins/{id}/*→ registry), app sidebar nav component (grep the file that renders nav items, e.g.components/app-sidebar/*),lib/ws/router.tslib/ws/client.ts(forward decoded messages to registry WS handlers), app root that reads boot payload and renders providers (grep forreadBootPayload/StateProvidermount — likelyapps/web/app/layout.tsxor a client bootstrap).
Acceptance
apps/web/components/plugins/plugin-slot.tsx:<PluginSlot name props/>renders allregistry.getSlotComponents(name)(each wrapped in an error boundary). Mount<PluginSlot name="task-sidebar"/>in the task detail sidebar and<PluginSlot name="settings-nav"/>in the settings nav (minimal, additive).apps/web/components/plugins/plugin-nav-items.tsx:<PluginNavItems/>rendersregistry.getNavItems()(section main) in the sidebar; clicking navigates toitem.path.- SPA route resolver: unknown path → check
registry.getRoutes(); render the plugin component inside the normal app shell. Settings resolver:/settings/ plugins/{id}/*→ registry settings routes. - WS bridge: incoming messages also dispatch to
registry.getWsHandlers(action). - Boot: on app start, after store + boot payload are ready,
installPluginGlobal(...)thenloadPlugins(bootPayload.plugins, buildHostApi)— gated onuseFeature("plugins")/bootPayload.pluginspresent. Idempotent (don't double-load on re-render). - Tests: PluginSlot renders registered components + isolates a throwing one; nav items render + navigate; route resolver returns a registered plugin route; WS bridge forwards to a registered handler.
Files
apps/web/components/plugins/plugin-slot.tsx+ testapps/web/components/plugins/plugin-nav-items.tsx+ testapps/web/lib/plugins/boot.ts(bootPlugins(bootPayload, storeApi, feature)) + test- edits:
src/spa-routes.tsx,src/settings-routes.tsx, sidebar nav file,lib/ws/router.ts, app bootstrap file
Verification
cd apps/web && pnpm run typecheck && pnpm lint && pnpm test -- plugin/mobile-parity: nav items + slot render responsively.
Output contract
Report each seam edited + how idempotent boot is guaranteed + slot names live. Keep edits additive (don't regress existing routes/nav). Follow apps/web/AGENTS.md.
Dependencies
task-18.