跳转至
  • frontend_vue3/src/types/plugin.ts(全新 · LoadedPlugin / PluginInfo / ModuleDescriptor TS interface)
  • frontend_vue3/src/composables/usePluginRegistry.ts(全新 · GET /api/plugins/list + load/unload/reload + Phase A mock)
  • frontend_vue3/src/stages/xilink/components/ModuleLibraryPanel.vue(改造 · 从 hardcode 列表 → API 数据源 · builtin/thirdparty 分类)
  • frontend_vue3/tests/composables/usePluginRegistry.test.ts(新增)
  • frontend_vue3/tests/components/ModuleLibraryPanel.test.ts(新增 · 若已存在则扩展) occupies: [P1.K-module-library(写), P1.K-types(写), P1.K-composables(写)] adr: docs/08-implementation/40-aios/ADR/ADR-AIOS-09-plugin-architecture.md ref_section: §2.3 PC 端动态加载机制 + §2.5 xivst 第三方插件 SDK + §4.2 fork 6 derived_from: ADR-AIOS-09 created: 2026-05-30 last_modified: 2026-05-30 12:18 dispatched_at: 2026-05-30 12:18 estimated: 1.5d unblocks:
  • 验证 ADR-09 §2.3 加载流程 ④(前端 ModuleLibrary 通过 IPC 拉取 ModuleRegistry 内容显示)
  • 给 ADR-09 第三方插件生态前端入口就位 related_zombie:
  • P0.U-meter-types-v3(9fc31c4 · 同部门前端 · types 扩展模式参考) related_dispatched:
  • P5.UA9-plugin-registry-service(同 ADR-09 后端配套 · /api/plugins/list 数据源 · 本任务先按 ADR contract 跑 mock 后切真接口)
  • P6.UA9-plugin-abi-v1-design(P6 · ABI 头 · 文件正交 · 字段定义最终源)
  • P6.UA9-cmake-per-module-refactor-phase1(P6 · 文件完全正交)

P1.UA9-module-library-from-registry · ModuleLibraryPanel API 数据源改造(ADR-AIOS-09 §2.3 fork)

Worker:TBD(用户分配 · isolation: 🧵 file · 主仓 04_development/ 任一空闲前端 worker)· 部门:前端 (frontend_vue3) 预计:1.5d · 优先级:P1 · 状态:dispatched 隔离:🧵 文件隔离(stages/xilink/components/ModuleLibraryPanel.vue + composables/usePluginRegistry + types/plugin.ts · 与 ADR-08 三前端 fork(stages/xilink/{bottom,drawers}/)+ ADR-12/ADR-11 stages 完全正交)


🔍 触发与解锁链

触发 状态 影响
ADR-AIOS-09 accepted v1.1 ✅ 2026-05-30 09:18 8 fork 启动 · 用户拍板派首批 4 fork
.clinerules v1.4 §任务隔离类型分配准则 本 fork 标 isolation: file(同 worktree 同 branch xistudio · 与 ADR-08 前端 fork 文件正交)
Phase A mock 策略 不强等 fork 5 P5.UA9-plugin-registry-service zombie · 内置 dev mock /api/plugins/list 响应
ADR §2.3 加载流程 ④ ✅ 永久 "前端 ModuleLibrary 通过 IPC 拉取 ModuleRegistry 内容显示" · 本任务实施

→ 跨栈独立(纯前端) · 不修改 contract-v1.0 · 与 ADR-08 三前端 fork(bottom/ + drawers/)文件正交 · 与 ADR-12/ADR-11 完全正交。


任务定义(基于 ADR-AIOS-09 §2.3)

按 ADR-09 §2.3 加载流程 ④ · 改造 ModuleLibraryPanel.vue:从 hardcode 模块列表 → 调 /api/plugins/list 拉取 LoadedPlugins + AllModules · 显示 builtin/thirdparty 分类(对齐 ADR §2.3 line 188-200 plugin 目录约定)。

  1. types/plugin.ts(全新 · 与后端 PluginInfo / ModuleDescriptor records 字段一对一)
  2. composables/usePluginRegistry.ts(全新 · REST poll + mock 双模式):
  3. loadAvailable() Promise<{ plugins, modules }>
  4. loadPlugin(dllPath) / unloadPlugin(uid) / reloadPlugin(uid)
  5. dev mock 内置 4-6 个 builtin 数据(对齐 fork 5 PluginInteropMock 数据)
  6. ModuleLibraryPanel.vue(改造 · 数据源切换):
  7. 删除 hardcode 模块列表(若有)
  8. onMounted 调 usePluginRegistry.loadAvailable()
  9. 按 plugin.pluginVendor 分两组:Builtin / Third Party
  10. 每个 module 显示:moduleName + category + 端口数 + 拖拽至画布
  11. loading 态 + 空状态占位

Phase A → Phase B 切换路径: - Phase A:usePluginRegistry 内置 dev mock(开发环境直接 hardcoded 4-6 个 plugin · 不依赖后端) - Phase B(fork 5 zombie 后):切真 fetch /api/plugins/list · UI 零改动


完整 prompt(直接复制粘贴 worker 终端)

[U-thread]   P1.UA9-module-library-from-registry(alias: P1.U-module-library-from-registry)
[部门]       前端 (frontend_vue3) · 推荐 skill: vuejs-typescript-best-practices
[Worker CWD] d:/work/25_claude/workspace/AlgoDepartment/04_development/(主仓 · 同 worktree 同 branch xistudio)
[Occupies]   P1.K-module-library(写 · ModuleLibraryPanel.vue 改造) + P1.K-types(写 · types/plugin.ts 全新) + P1.K-composables(写 · usePluginRegistry)
[隔离]       🧵 文件隔离(.clinerules v1.4 §任务隔离类型分配准则)· 仅写:
             - frontend_vue3/src/types/plugin.ts(全新)
             - frontend_vue3/src/composables/usePluginRegistry.ts(全新)
             - frontend_vue3/src/stages/xilink/components/ModuleLibraryPanel.vue(改造 · 现有壳)
             - frontend_vue3/tests/composables/usePluginRegistry.test.ts + tests/components/ModuleLibraryPanel.test.ts(新增)
             严禁动 stages/xilink/bottom/*(P1.UA8-link-error-check 拥有) · stages/xilink/drawers/*(P1.UA8-perf-monitor + runtime-selector 拥有)· stages/xitest/* / stages/xitune/*
[优先级]     P1 · 1.5d · ADR-09 fork 6 · 跨栈独立 · Phase A mock 先跑(不强等 fork 5 zombie)· Phase B 切真接口零改动
[ADR]        d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/ADR/ADR-AIOS-09-plugin-architecture.md(必读 §2.3 + §2.5 xivst SDK + §4.2 fork 6)
[业务行为契约引用] ADR-AIOS-09 §2.3 加载流程 ④(line 213) + §2.2 ABI 字段对齐(C# 端 PluginInfo / ModuleDescriptor)
[参考文档](绝对路径)
  - d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/ADR/ADR-AIOS-09-plugin-architecture.md(主 ADR · §2.3 必读)
  - d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/prompts/active/P5.UA9-plugin-registry-service.md(后端配套 · REST schema 真值源)
  - d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/prompts/active/P6.UA9-plugin-abi-v1-design.md(同期 P6 fork · ABI 字段最终定义)
  - d:/work/25_claude/workspace/AlgoDepartment/04_development/frontend_vue3/src/stages/xilink/components/ModuleLibraryPanel.vue(壳已存在 · 看现有数据源 + 拖拽实现)
  - d:/work/25_claude/workspace/AlgoDepartment/04_development/frontend_vue3/src/composables/(看现有 use*WS / useRuntimeTarget / useLinkErrorWS / useMetricsWS 风格)
  - 同部门标本(强制 read · 4 维度对齐):d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/prompts/active/P1.UA8-runtime-selector-ui.md(同部门前端 · ADR-08 §2.4 · REST + WS 双源 + Phase A/B 切换模式参考)
  - d:/work/25_claude/workspace/AlgoDepartment/04_development/dsp_algo/modules/(列目录 · mock 数据参考实际 builtin 名)
  - contract-v1.0(已 frozen · 不改):d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/contracts/protocol-v1.md

【背景】
ADR-AIOS-09 accepted v1.1 · 议题 ⑥ 动态加载架构 · §2.3 加载流程 ④ "前端 ModuleLibrary 通过 IPC 拉取 ModuleRegistry 内容显示"。当前 ModuleLibraryPanel.vue 用 hardcode 模块列表(无法体现 builtin / thirdparty 分类) · 本任务改造为 API 数据源。

🚨 **真值核查**:
- 当前 frontend_vue3/src/stages/xilink/components/ModuleLibraryPanel.vue 用 hardcode 列表
- 无 types/plugin.ts(0 命中)
- 与 ADR-09 后端 fork 5 + P6 fork 1 ABI 头依赖 · 但本任务用 dev mock 解耦 · 不强等

**Phase A mock 策略**(不强等 fork 5 zombie):
- usePluginRegistry 内置 dev mock(import.meta.env.DEV 时直接返回 hardcoded 4-6 个 plugin)
- 模拟 abiMajor=1 / abiMinor=0 / pluginVendor="Xisound"(builtin)+ 1-2 个 "Vendor X" 第三方
- 单测覆盖 mock 数据渲染 + 分类逻辑

**Phase B 切换**(fork 5 zombie 后):
- usePluginRegistry 切 fetch('/api/plugins/list') · 删除 mock 数据 · UI 零改动
- 切换点:usePluginRegistry.ts 一处函数体修改

**ADR §2.3 加载流程 ④ 关键点**(line 213):
- "前端 ModuleLibrary 通过 IPC 拉取 ModuleRegistry 内容显示"
- IPC = REST `/api/plugins/list`(不是 WS · 因为 plugin 列表低频变更)
- 显示 builtin/thirdparty 分类(对齐 ADR §2.3 line 188-200 plugin 目录约定:plugins/builtin/ vs plugins/thirdparty/)

**契约策略**:不修改 contract-v1.0(已 frozen) · `/api/plugins/*` REST 走 dev-api · K2-protocol-v2 §plugin-loading 起草时正式入。

【执行步骤】

Step 1 · read 已有基础(只读 · 必做)
- read ADR-AIOS-09 §2.3 全文(line 187-242 · 加载流程 ④ + 插件目录约定)+ §2.5 xivst SDK(builtin/thirdparty 分类语义)
- read 同期 P5.UA9-plugin-registry-service.md(看 REST schema · /api/plugins/list 返回 { plugins, modules } · 字段名 camelCase)
- read 同期 P6.UA9-plugin-abi-v1-design.md(看 ABI 字段最终定义 · 本任务 TS interface 一对一)
- read frontend_vue3/src/stages/xilink/components/ModuleLibraryPanel.vue 当前实现(看 hardcode 数据源 + 拖拽 emit)
- read frontend_vue3/src/composables/useLinkErrorWS.ts / useMetricsWS.ts / usePerfPerModule.ts / useRuntimeTarget.ts(同部门 · 看 Phase A mock + Phase B 切换风格)
- read 标本 prompts/active/P1.UA8-runtime-selector-ui.md(同部门 · ADR-08 §2.4 · REST poll + Phase A/B 切换模式)
- ls dsp_algo/modules/ 看实际 builtin 名(mock 数据用)

Step 2 · 新建 frontend_vue3/src/types/plugin.ts(与后端 PluginInfo / ModuleDescriptor 字段一对一)
- export interface PluginInfo {
    abiMajor: number              // 必须 1(本 ADR 仅支持 ABI v1)
    abiMinor: number
    pluginUid: string             // 反向域名 com.xisound.builtin.<name> / com.<vendor>.<plugin>
    pluginName: string
    pluginVersion: string         // 语义化 1.0.0
    pluginVendor: string          // "Xisound"(builtin)/ 其他(thirdparty)
    moduleCount: number
  }
- export interface ModuleDescriptor {
    moduleUid: string             // 对齐 ADR-04 §2.1.2 反向域名
    moduleName: string
    category: 'source' | 'sink' | 'eq' | 'dynamics' | 'utility' | 'custom'
    inputPortCount: number
    outputPortCount: number
    paramCount: number
    flags: number                 // bit0=supports_pc / bit1=supports_dsp / bit2=stateful
  }
- export interface LoadedPlugin {
    pluginUid: string
    filePath: string
    info: PluginInfo
    loadedAt: string              // ISO timestamp
    status: 'loaded' | 'failed' | 'unloading'
  }
- export interface PluginRegistrySnapshot {
    plugins: LoadedPlugin[]
    modules: ModuleDescriptor[]
  }
- export type PluginVendorKind = 'builtin' | 'thirdparty'
- helper:isBuiltinPlugin(plugin: LoadedPlugin) => plugin.info.pluginVendor === 'Xisound'

Step 3 · 新建 frontend_vue3/src/composables/usePluginRegistry.ts(REST poll + mock 双模式)
- 提供:
  · loadAvailable() Promise<PluginRegistrySnapshot> — Phase A mock / Phase B GET /api/plugins/list
  · loadPlugin(dllPath: string) Promise<{ success, error?, abiMismatch? }>
  · unloadPlugin(pluginUid: string) Promise<void>
  · reloadPlugin(pluginUid: string) Promise<void>
  · plugins: Vue ref<LoadedPlugin[]>
  · modules: Vue ref<ModuleDescriptor[]>
  · isLoading: Vue ref<boolean>
  · error: Vue ref<string | undefined>
- Phase A mock 数据(import.meta.env.DEV 时启用):
  - 4 个 builtin plugin:com.xisound.builtin.{eq, dynamics, source, sink}(pluginVendor="Xisound")
  - 1 个 thirdparty plugin 示例:com.example.demo.reverb(pluginVendor="Example Co.")
  - 每 plugin 含 1-3 个 module(moduleUid 用反向域名)
- Phase B fetch 实现(import.meta.env.PROD 或 mock=false 时):
  - const res = await fetch('/api/plugins/list')
  - 错误处理:网络错 / 后端 500 → error ref 设置 · UI 显示降级
- TODO 注释:Phase B 切换点明确(usePluginRegistry.ts 一处)
- 返回 reactive 对象 · 给 ModuleLibraryPanel watch

Step 4 · 改造 frontend_vue3/src/stages/xilink/components/ModuleLibraryPanel.vue(数据源切换)
- 删除 hardcode 模块列表(保留模板风格 · 仅换数据源)
- onMounted 调 usePluginRegistry.loadAvailable() · 监听 plugins / modules ref
- 计算属性 builtinModules / thirdpartyModules:
  ```ts
  const builtinModules = computed(() =>
    modules.value.filter(m => {
      const plugin = plugins.value.find(p => m.moduleUid.startsWith(p.pluginUid + '.'))
      return plugin?.info.pluginVendor === 'Xisound'
    })
  )
  ```
- 模板:两段 [Builtin] / [Third Party] 折叠组(若 thirdparty 为空则隐藏)· 每段下列 module 卡片
- 卡片显示:moduleName + category 标签 + 端口数(N→M)+ 拖拽至画布(emit('drag-start', module))
- loading 态:isLoading=true 时显示 skeleton 占位
- 空状态:无 module 时显示"暂无插件 · 请检查 plugins/ 目录"
- 保留现有拖拽 emit 接口(向后兼容主画布逻辑)

Step 5 · 单元测试 + e2e mock 真值
- 新增 tests/composables/usePluginRegistry.test.ts(>= 5 case:loadAvailable mock 返回 4-6 plugin / loadPlugin 成功 / loadPlugin abi mismatch / unloadPlugin / 错误处理 fetch 500)
- 新增 tests/components/ModuleLibraryPanel.test.ts(若已存在则扩展):
  · >= 4 case:渲染 builtin 4 个 / 渲染 thirdparty 分类 / 拖拽 emit drag-start / 空状态占位
- 至少 9 个新 case
- typecheck + build + test:unit 基线 356/3 → 目标 ≥ 365/3(+9 用例)

Step 6 · 端到端真值(.clinerules v1.8 §业务行为契约必填段)
- vitest 真值脚本:mount ModuleLibraryPanel · usePluginRegistry mock 注入 4 builtin + 1 thirdparty → 断言:
  · [Builtin] 段显示 4 个 plugin 的 module(eq / dynamics / source / sink)
  · [Third Party] 段显示 1 个 plugin 的 module
  · 点击 module 卡片 → emit('drag-start', { moduleUid, ... })
- ABI 兼容验证:注入 abiMajor=2 的 mock plugin → loadPlugin 返回 abiMismatch=true · UI 显示警告

Step 7 · Commit + push
- git status / git pull origin xistudio --no-rebase / git add frontend_vue3/src/types/plugin.ts frontend_vue3/src/composables/usePluginRegistry.ts frontend_vue3/src/stages/xilink/components/ModuleLibraryPanel.vue frontend_vue3/tests/composables/usePluginRegistry.test.ts frontend_vue3/tests/components/ModuleLibraryPanel.test.ts
- git commit -m "..."(见下)
- git push origin xistudio

【验收】

形式合规:
- npm run typecheck → ✓ exit 0
- npm run build → ✓ 零错误
- npm run test:unit → ≥ 365/3(基线 +9 用例)
- 5 文件落地(types ×1 + composables ×1 + ModuleLibraryPanel ×1 改 + tests ×2)

业务行为契约自查清单(ADR §2.3):
- [ ] ① types/plugin.ts 字段严格对齐后端 PluginInfo / ModuleDescriptor / LoadedPlugin(camelCase TS)
- [ ] ② usePluginRegistry Phase A mock 4 builtin + 1+ thirdparty(对齐 dsp_algo/modules/ 实际 builtin 名)
- [ ] ③ ModuleLibraryPanel 改 API 数据源(删除 hardcode)
- [ ] ④ Builtin / Third Party 分两段显示(按 plugin.pluginVendor === 'Xisound' 判定)
- [ ] ⑤ 拖拽 emit drag-start 接口向后兼容(主画布拖拽不破坏)
- [ ] ⑥ ABI 兼容检查:abiMajor != 1 时 loadPlugin 返回 abiMismatch=true · UI 显示警告
- [ ] ⑦ Phase A mock 跑通 · ① schema 与 Phase B 真接口零改动切换
- [ ] ⑧ loading 态 skeleton + 空状态占位

【commit】
subject:`feat(P1.UA9-module-library-from-registry): ModuleLibraryPanel API data source + builtin/thirdparty classify(Phase A mock) · ADR-09 §2.3 frontend`

trailer(必须精确):
[step=7/7] [pid=P1] [uid=UA9-module-library-from-registry] [occupies=P1.K-module-library+P1.K-types+P1.K-composables]
[files=frontend_vue3/src/types/plugin.ts, frontend_vue3/src/composables/usePluginRegistry.ts, frontend_vue3/src/stages/xilink/components/ModuleLibraryPanel.vue, frontend_vue3/tests/composables/usePluginRegistry.test.ts, frontend_vue3/tests/components/ModuleLibraryPanel.test.ts]
[ipc=api/plugins/list(consumer · Phase A mock → Phase B fetch), api/plugins/load, api/plugins/unload, api/plugins/reload]
[isolation] file(同 worktree 同 branch · 文件正交于 ADR-08 三前端 fork + ADR-12/ADR-11 + ADR-09 P5/P6 fork)
[phase] A=mock(usePluginRegistry internal hardcoded data) → B=real(fetch /api/plugins/list · 等 fork 5 zombie)
[derived_from] ADR-AIOS-09 §2.3 fork 6

【禁止】
1. ❌ **不许动 stages/xilink/bottom/* / drawers/***(P1.UA8 三 fork 拥有)
2. ❌ **不许动 stages/xitest/* / stages/xitune/***(ADR-12 / ADR-11)
3. ❌ **不许嵌入完整 Vue SFC 骨架**(.clinerules v1.6 · worker 自决)
4. ❌ **不许在 types/plugin.ts 加 ADR §2.2 未提的字段**(严格对齐 ABI)
5. ❌ **不许直接调 LoadLibrary / DllImport**(纯前端 · 通过 REST 间接)
6. ❌ **不许动 backend_csharp/ 或 dsp_algo/ 任何文件**(P5/P6 fork 拥有)
7. ❌ **不许修改 contracts/protocol-v1.md**(已 frozen · §plugin-loading 入 v2)
8. ❌ **不许破坏现有拖拽 emit 接口**(主画布 ModuleNode 创建逻辑依赖)
9. ❌ **不许跳验收**(typecheck/build/test 任一红必须修到全绿)
10. ❌ **不许省略三元组 trailer**(含 [phase] + [isolation] 特殊标记)
11. ❌ **不许自改本 prompt 文件**

解锁链(本任务 zombie 后)

  • ✅ ADR-09 §2.3 加载流程 ④ 前端闭环验证(ModuleLibrary 改 API 数据源 + 分类显示)
  • ✅ Phase B 切真接口零改动(fork 5 P5.UA9-plugin-registry-service zombie 后 · usePluginRegistry 一处切换)
  • ✅ ADR-09 第三方插件生态前端入口就位(thirdparty 分类显示 · 后续 xivst SDK 1.0 发布后用户可见)
  • ✅ 给 K2-protocol-v2 §plugin-loading 起草提供前端实测 schema

风险评估

风险 缓解
⚠️ Phase A mock 字段与 fork 1 plugin_abi.h v1 / fork 5 PluginInfo C# record 字段不一致 step 1 强制 read fork 1 + fork 5 prompt · 字段一对一 · commit 前 grep 关键字段名
⚠️ ModuleLibraryPanel 现有拖拽 emit 接口被破坏 step 4 保留现有 emit('drag-start', module) 签名 · 主画布 ModuleNode 创建零修改
⚠️ builtin / thirdparty 判定逻辑不一致 helper isBuiltinPlugin(plugin) === plugin.info.pluginVendor === 'Xisound' · 单测覆盖
⚠️ Phase B 切换时 mock 数据残留 usePluginRegistry.ts dev/prod 分支明确 · 单测覆盖 prod 模式调真 fetch
⚠️ ABI v1 frozen 后未来字段扩展 TS interface 用可选字段 ?: 兼容未来 minor+ ABI · 但本任务 v1 字段必填

历史

时间 事件 hash
2026-05-30 12:18 dispatched · ADR-09 4 fork 首批派发(B 方向 · fork 6 前端 · Phase A mock 不强等 fork 5 zombie · isolation: file 同 worktree)· UID v1.4 命名 P1.UA9