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