ADR-AIOS-05 · XiStudio Workspace 架构决议
1. 上下文(Context)
1.1 触发事件
2026-05-26 用户在 ADR-AIOS-04(XiForge)落地后的次日,提出 XiStudio 跨 4 stage 的 workspace 文件格式与项目组织问题。经过 3 轮对话 + 5 份并行 subagent 真值调研,发现当前架构存在 7 个相互关联的根本性问题,需要一次性决议:
| # | 用户原话(精简) |
|---|---|
| 1 | "workspace 下 link.json 由 preset.json · 当前怎么区分文件是个问题" |
| 2 | "建议:文件后缀有识别性 · .xilink / .xiforge / .xitest / .xitune" |
| 3 | "XiForge 生命周期短 · 一旦 module 壳子做好就基本不用 · 后续放菜单栏中" |
| 4 | "第三方 xivst 插件:AWE 做法是目录管理 · 我们也可类似(本地导入 + 云端 registry)" |
| 5 | "4 种调音模式":正向开发(试车 DSP / PC 模拟)+ 兼容模式 + 反向调音 |
| 6 | "已固化在 dsp 中可读取链路结构 · 用来确认是否符合预期"(refresh_link 校验语义) |
| 7 | "preset 的 profile manifest 呢?是不是还需要有一个 .xiprofile" |
1.2 5 份 subagent 真值调研发现的关键事实
第一轮(2 份 · 代码真值):
- ✅ 已有 2 个 workspace store(workspaceStore.ts 主工程 + xiForgeWorkspaceStore.ts IP 工程)· 行为完全后端化(WS 协议)
- ❌ 当前全部用 .json · 无任何自定义后缀(用户提议是全新设计)
- ✅ 发 link 协议齐全:write_link(主推 · 4 处触发)+ apply_link(结构性参数变更)
- ⚠️ read_link 协议仅在测试中用(xitest/IntegrationTestPanel.vue) · 未用于运行时 store 同步
- ❌ TuningMode 区分几乎不存在(仅 engineMode: 'pc_sim' | 'embedded' | 'unknown' 是只读字段 · 来自后端)
- ❌ 反向调音占位 = 0(全局扫描 0 命中 auto.*tuning|reverse.*tuning|inverse.*tune)
第二轮(2 份 · 业界对标):
- 🎯 AWE Module Library:目录扫描 + 元数据注册(非 Windows 注册表) · 启动时聚合
- 🎯 AWE Tuning Mode vs Standalone Mode 直接对应用户场景 2a-ii vs 2a-i
- 🎯 VST3 Preset:永远独立 .vstpreset · 工程内嵌为 chunk(给 XiStudio 强烈启示)
- 🎯 Pro Tools / Logic Pro 都用项目目录模式(主文件 + sibling 媒体目录)
第三轮(1 份 · Profile 系统 · v1.1 新增):
- ✅ ProfileSidebar.vue 1046 行 @ src/stages/xitune/(已成熟)
- ✅ 数据结构:AmbianceProfile { profileId, name, icon, modulePresets: Record<instanceId, presetId> }
- ⭐ Profile 与 Preset 是 1:N 关系:Profile 不含 preset 实参 · 只持有"instanceId → presetId"映射
- 🚨 Profile 依附 link(因为 key 是 instanceId)→ .xiprofile 必须引用 .xilink + linkUid 双重校验
- ❌ Profile 当前与 preset 共用 store · 无独立持久化
1.3 用户的关键产品定义(写入代码注释)
normal 模块 = 带链路信息(参数走 set_params 协议 · 通过 link 路由) · 承接 ADR-AIOS-04
legacy 模块 = 不带链路信息(参数走 legacy_set_param + PID 直接寻址)
TuningMode 4 种(本 ADR 新增): -
pc_simulation= 后端 PC 跑 · 必须发 link -target_live= DSP 已固化 link · 不发 link · 可刷新校验 -legacy_compat= 兼容模式 · 链路装饰性 · 仅 PID 调参 -auto_inverse= 反向调音 · 算法生成 link + preset(实验性)
refresh_link协议双语义: - 加载时反读(target_live 自动) - 校验/对账(pc_simulation + target_live 用户主动 · legacy/auto_inverse 不适用)
1.4 7 项决议拍板状态(用户原话:"决议都按你的建议来 · 启动 ADR")
| # | 决议 | 选择 | 状态 |
|---|---|---|---|
| 1 | 文件后缀体系 | 7 后缀方案(.xistudio/.xilink/.xitune/.xitest/.xiforge/.xipreset/.xiprofile ⭐) | ✅ |
| 2 | 项目目录结构 | 方案 A · 项目目录 + .xistudio manifest + profiles/ 子目录 | ✅ |
| 3 | XiForge 独立 + 第三方插件 | 全局后台目录 · 类 AWE 扫描 · 本地导入(P2)+ 云端 registry(P3) | ✅ |
| 4 | 4 种 TuningMode | pc_simulation / target_live / legacy_compat / auto_inverse | ✅ |
| 5 | Preset+Profile 三层架构 | .xitune 引用 .xiprofile 引用 .xipreset |
✅ |
| 6 | 保存/加载 API | 小文件走 WS 协议 · 批量导出 Blob + a[download] | ✅ |
| 7 | 起 ADR-AIOS-05 | 是(本文) | ✅ |
2. 决议(Decision)
2.1 主决议 · 5 个架构级方向
2.1.1 7 后缀文件体系 + 项目目录模式
文件后缀体系(全部以 .xi 开头 · 不带 .json 子后缀):
*.xistudio ← 项目主清单(轻量 manifest · JSON)
*.xilink ← 链路定义(XiLink 输出)
*.xitune ← 调音工程(单一调音状态 · 引用 .xilink)
*.xitest ← 测试用例集(独立)
*.xiforge ← 模块定义(独立 · 全局后台目录)
*.xipreset ← 单模块参数集(类 .vstpreset · 跨工程可复用)
*.xiprofile ← 多 preset 组合方案(依附 .xilink · 类 DAW Channel Strip Preset)⭐ v1.1
项目目录推荐结构:
my-amplifier-project/
├── my-amplifier.xistudio ← manifest 入口
├── link/main.xilink
├── tuning/{default,tuner-a}.xitune
├── profiles/{jazz,classical,movie}-scenario.xiprofile
├── presets/{eq-jazz-warm,comp-soft,reverb-hall}.xipreset
├── tests/{smoke,electrical,acoustic}.xitest
└── assets/{target-curves,reference-audio}/
目录语义区分:
- presets/ — 跨项目可复用的单模块参数(类 VST3 plugin preset library)
- profiles/ — 依附本项目 link的多模块组合方案(用户切场景 jazz/classical/movie)
- assets/ — 资源文件(目标曲线 / 参考音频)
2.1.2 4 种 TuningMode + linkStore 双状态扩展
类型定义(src/types/tuningMode.ts 新建):
export type TuningMode =
| 'pc_simulation' // 场景 2a-ii · PC 模拟 · 后端在 PC 运行 · 必须发 link
| 'target_live' // 场景 2a-i · 试车 DSP · link 已固化 · 仅参数实时调
| 'legacy_compat' // 场景 2b · 兼容模式 · 链路象征 · 仅 PID 调参
| 'auto_inverse' // 场景 2c · 反向调音 · 算法生成(实验性 · P3)
linkStore 双状态扩展:
interface LinkState {
// 编辑态(host 草稿 · 用户在 XiLink 改的就是这个)
draft: { nodes, edges, instances }
// 部署态(DSP 实际运行的 · 通过 refresh_link 反读得到)
deployed?: {
nodes, edges, instances,
syncedAt: string,
source: 'pc_backend' | 'target',
}
// target_live 模式:UI 显示 deployed · 不允许编辑结构 · 只允许改参数
}
4 种模式数据流对照:
| 模式 | XiTune 加载策略 | linkStore 来源 | refresh_link 用途 |
|---|---|---|---|
| pc_simulation | 加载 .xilink(读+写) |
linkStore.draft = .xilink | ⭐ 校验/对账 |
| target_live | 启动 refresh_link | linkStore.deployed = DSP 反读 | ⭐ 校验链路 |
| legacy_compat | 不加载 .xilink | 仅装饰性占位 | ❌ 不适用 |
| auto_inverse | 算法生成 → linkStore.draft | linkStore.draft = 算法输出 | ❌ 不适用 |
2.1.3 Preset+Profile 三层架构(v1.1 核心)
┌──────────────────────────────────────────────────────────┐
│ .xitune(完整调音状态 · 工程级) │
│ ├ tuningMode: 'pc_simulation' / 'target_live' / ... │
│ ├ linkRef: '../link/main.xilink' │
│ ├ activeProfileRef?: '../profiles/jazz-scenario.xiprofile' │
│ └ paramOverrides: { ... }(临时调音 · 未保存的微调) │
└──────────────────────────────────────────────────────────┘
↓ 引用
┌──────────────────────────────────────────────────────────┐
│ .xiprofile(场景方案 · 多模块组合) │
│ ├ name: "Jazz Scenario" / icon / scenario / linkRef │
│ ├ linkUid: "0xC0FFEE12345"(双重校验 · 防 instanceId 失效) │
│ └ modulePresets: { instanceId → preset 文件路径 } │
└──────────────────────────────────────────────────────────┘
↓ 引用
┌──────────────────────────────────────────────────────────┐
│ .xipreset(单模块参数集 · 跨项目可复用) │
│ ├ moduleUid: 0x00000200(ADR-AIOS-04 UID) │
│ ├ moduleVendor / moduleVersion(兼容性校验) │
│ └ params: { gain: 6.5, freq: 440, q: 1.2, ... } │
└──────────────────────────────────────────────────────────┘
用户工作流:
1. 调好 EQ 模块参数 → "另存为 Preset" → eq-jazz-warm.xipreset(单模块)
2. 调好 N 个模块 → "另存为 Profile" → jazz-scenario.xiprofile(组合方案 · 引用 N 个 preset)
3. 切换场景 → 加载 Profile "Classical Scenario" → 一键改 N 个模块的 preset 选择
Profile schema(必须含 linkUid 双重校验):
{
"version": "1.0",
"profileId": "uuid-xxxx",
"name": "Jazz Scenario",
"icon": "🎷",
"scenario": "jazz",
"linkRef": "../link/main.xilink",
"linkUid": "0xC0FFEE12345",
"createdAt": "2026-05-26T10:25:00Z",
"modulePresets": {
"instance-0xC0FFEE0001": "../presets/eq-jazz.xipreset",
"instance-0xC0FFEE0002": "../presets/comp-soft.xipreset"
}
}
2.1.4 XiForge 独立 + 第三方插件目录(类 AWE)
全局后台目录结构:
~/Library/XiStudio/Modules/ ← 类 AWE Module Library
├── builtin/ ← XiStudio 内置(随版本)
│ ├── source/source_*.xiforge
│ ├── filter/geq_v1.xiforge
│ └── ...
├── thirdparty/ ← 第三方 vst 插件目录
│ ├── acmecorp_compressor/
│ │ ├── manifest.xiforge
│ │ ├── compressor.dll / .so ← 算法二进制
│ │ └── compressor.c ← 源码(可选 · codegen)
│ └── studiomax_reverb/
└── user-custom/ ← XiForge 自建模块
└── my-custom-eq.xiforge
加载机制:XiStudio 启动时扫描 3 个子目录 → 全部注入 moduleDefStore · 项目通过 ModuleDef.uid(ADR-AIOS-04 UID)解析
关键洞察:.xiforge 不在项目目录中 · 在全局后台库 · 项目只通过 UID 引用模块 · 这彻底解决"XiForge 不和其他 workspace 耦合"
2.1.5 refresh_link 协议双语义(v1.1 用户澄清)
refresh_link 协议有 2 种语义:
- 加载时反读(target_live 模式专用):
-
打开
.xitune· 模式为 target_live → 自动调用refresh_link从 DSP 反读链路结构 → 填充 linkStore.deployed -
校验/对账(pc_simulation + target_live 都可触发):
- 用户主动点 "刷新链路" 按钮 →
refresh_link反读 → 与 host 当前 linkStore 比较 → 显示差异(missing modules / param drift / connection mismatch) - 用途:确认 DSP / PC backend 加载的 link 与 host 工程一致(对账机制)
- legacy_compat / auto_inverse 不适用(legacy 无真实 link · auto_inverse 数据流反向)
2.2 本季度实施范围
P0 + P1 共 7 项任务 · 总 10d · 跨 4 agent 并行 6-8 天:
| 优先级 | 任务 | 工作量 | Agent |
|---|---|---|---|
| P0 | 7 后缀文件体系 + .xistudio manifest 设计 | 1d | ClaudeA |
| P0 | 项目目录结构(含 profiles/) + workspaceStore 升级 | 1.5d | ClaudeA |
| P0 | TuningMode 枚举 + linkStore draft/deployed 双状态 | 1.5d | ClaudeC |
| P0 | refresh_link 协议(双语义) · 前后端配合 | 2d | ClaudeC + 后端 ClaudeB |
| P1 | XiTune 4 模式 UI 切换 + 校验差异 UI | 2d | ClaudeC |
| P1 | .xipreset 独立文件保存/加载(类 VST3) | 1d | ClaudeC |
| P1 | ⭐ .xiprofile 独立文件 + linkUid 双重校验 | 1d | ClaudeC |
推迟到下季度(P2)+ 长期(P3): - P2:第三方 vst 插件本地导入(2d) + XiForge 移到菜单(1d) - P3:auto_inverse 反向调音占位(3d) + 云端 vendor registry(10d+)
2.3 4 agent 边界铁律(继承 ADR-AIOS-03)
| 资源 | 写权限 | 备注 |
|---|---|---|
src/types/tuningMode.ts(新建) |
ClaudeC 独占 | 4 种模式定义 |
src/types/workspace.ts(新建) |
ClaudeA 独占 | 7 后缀类型 |
src/stores/linkStore.ts |
ClaudeA 改 draft/deployed 双状态 | 协调 ClaudeC |
src/stores/workspaceStore.ts |
ClaudeA 升级 manifest 支持 | - |
src/stages/xitune/ProfileSidebar.vue |
ClaudeC 独占 | .xiprofile 持久化 |
src/stages/xitune/(整目录) |
ClaudeC 独占 | 4 模式 UI |
src/stages/xilink/(整目录) |
ClaudeA 独占 | 文件加载入口 |
~/Library/XiStudio/Modules/ 扫描逻辑 |
ClaudeA 独占 | 第三方插件(P2 后) |
后端 refresh_link 协议 |
后端 ClaudeB 独占 | 必须配合前端 |
2.4 派发顺序
Day N(本周内 · P0 起手):
├── ClaudeA:7 后缀类型 + .xistudio manifest → 1d
├── ClaudeC:TuningMode 类型 + linkStore 双状态 → 1.5d
└── 后端 ClaudeB:refresh_link 协议设计 → 1d(协调)
Day N+2:
├── ClaudeA:项目目录 + workspaceStore 升级 → 1.5d
├── ClaudeC:XiTune 4 模式 UI 起手 → 2d
└── 后端 ClaudeB:refresh_link 实现 → 1d
Day N+5:
├── ClaudeC:.xipreset / .xiprofile 持久化 → 2d
└── 校验差异 UI 收尾
Day N+7:
└── 收口 e2e + 视觉对照 · P0+P1 完成
3. 备选方案(Alternatives Considered)
3.1 方案 A · 单文件 zip 容器(类 Ableton .als)
否决。 - 不利于 git 版本管理(二进制 zip 无法 diff) - 单文件出错时整个工程损坏 - 不适合"工程师 + 调音师协作"的多角色场景
3.2 方案 B · 纯独立 stage 文件(用户原始建议)
否决(用户已拍板转 1A)。 - 无法表达"同一项目的不同 stage 文件之间的关联" - 项目级别的 manifest 缺失 · 用户难以管理多 stage 文件 - 不利于 .xiprofile 这种依附 link 的文件
3.3 方案 C · TuningMode 二元简化(只 normal vs legacy)
否决。 - 用户场景 2 明确包含 4 种(试车 / PC 模拟 / 兼容 / 反向) - 二元模型无法支持反向调音的草稿态 - 与 AWE Tuning Mode + VST 三态业界标准不符
3.4 方案 D · Profile 内嵌 preset 参数(不引用 .xipreset)
否决。 - 失去 preset 跨工程复用能力(类 VST3 启示) - 一个 preset 改了 · N 个 profile 的对应字段都要更新(维护噩梦) - 与 DAW Channel Strip Preset 业界做法不符
3.5 方案 E · refresh_link 仅单一语义(只反读 不校验)
否决(用户澄清后修正)。 - 用户原话:"已固化在 dsp 中可读取链路结构 · 用来确认是否符合预期" - 校验/对账语义对 pc_simulation 模式也很重要(后端可能加载错 link) - 双语义实现成本相同 · 缺一会有功能缺失
4. 后果(Consequences)
4.1 正面后果
- 文件类型识别清晰:7 后缀都以
.xi开头 · 视觉一致 · 用户能秒辨文件类型 - 项目可 git 管理:目录模式 + 文本 JSON · 可逐文件 diff
- 跨角色协作友好:算法工程师改
.xilink· 调音师改.xitune/.xiprofile· 不冲突 - 4 种 TuningMode 是产品差异化:超越简单的"读/写 link"二元 · 直接对标 AWE Tuning Mode
- Preset 跨工程复用:类 VST3 模式 · 提高调音师效率
- 第三方插件生态可扩展:类 AWE 目录扫描 · 为下半年 vendor registry 留口
- refresh_link 校验:解决"DSP 加载的 link 与 host 工程不一致"低级 bug
- 本季度可落地:P0+P1 共 10d · 跨 4 agent 并行 6-8 天
4.2 负面后果
- 重构成本:workspaceStore + linkStore + 多个 dialog 都要改 · 需要充分回归测试
- 后端协调:
refresh_link协议必须前后端同步落地 · 跨 CLI 协调成本上升 - migration 兼容:现有
.json文件需要迁移工具(自动检测旧格式 + 转换)· 1 周内可能有遗留文件 - Profile 双重校验逻辑复杂:linkRef + linkUid + instanceId 一致性校验 · 需要单元测试覆盖
- TuningMode UI 设计挑战:4 种模式切换器 + 各模式的差异化 UI 状态 · 设计师压力
4.3 风险缓解
| 风险 | 触发条件 | 预案 |
|---|---|---|
| .xiprofile 加载时 link 已被替换 | 用户改了 link 后切 profile | linkUid 双重校验 → 提示 "Profile incompatible with current link" + 列出失效 instanceId |
| refresh_link 协议前后端不同步 | 后端 ClaudeB 延期 | 前端先做 stub(返回 mock 数据) · 后端就绪后切真实 |
旧 .json workspace 迁移失败 |
用户有 5+ 个旧项目 | 提供"迁移向导"对话框 · 一键转换 · 自动备份原文件到 .json.bak |
| target_live 模式下 DSP 读不到 link | DSP 固件没实现 read_link 应答 | UI 降级:linkStore.deployed 为空 + 提示"DSP firmware needs upgrade" |
| Profile 命名冲突(同 scenario) | 用户多次保存"jazz" | 自动加序号 jazz-1 / jazz-2(类 macOS Finder 重名处理) |
4.4 不影响
- ADR-AIOS-04(XiForge)的 9 项决议保持不变 · 本 ADR 与之互补
- ADR-AIOS-03(4 agent 按 stage 分工)保持不变 · 本 ADR 在其框架内执行
legacy_set_param后端协议保留(对应 legacy_compat 模式)- 现有 ProfileSidebar.vue(1046 行)不丢弃 · 仅升级持久化路径(从内存 → .xiprofile 文件)
5. 命名规范(Naming Convention)
5.1 文件后缀(7 种)
.xistudio— 项目主清单(每个项目目录唯一一个).xilink— 链路定义.xitune— 调音工程(可多个 · tuner-a/tuner-b).xitest— 测试用例集.xiforge— 模块定义(全局后台 · 不在项目目录).xipreset— 单模块参数集(跨工程可复用).xiprofile— 多 preset 组合方案(依附 link · 项目内)
5.2 命名规则
- 项目名:kebab-case(
my-amplifier-project) - stage 文件名:kebab-case · 描述用途(
main.xilink/tuner-a.xitune/jazz-scenario.xiprofile) - Preset 文件名:
<module-type>-<style>.xipreset(如eq-jazz-warm.xipreset/comp-soft.xipreset) - Profile 文件名:
<scenario>-scenario.xiprofile(如jazz-scenario.xiprofile)
5.3 TuningMode 字符串(4 种)
'pc_simulation'(snake_case · 与协议层一致)'target_live''legacy_compat''auto_inverse'
5.4 全局唯一标识
- 项目 UID:
0xC0FFEE12345(12 位十六进制 · 用户可见) - ModuleDef UID:32 位(承接 ADR-AIOS-04)
- ProfileId / PresetId:UUID v4
6. 待决议项(本 ADR 已 accepted · 仅留实施期可能浮现的子问题)
- ☐ 旧
.jsonworkspace 迁移向导是否需要单独 sprint?(P-1 实施时核实) - ☐
refresh_link校验差异的 UI 设计(diff view · 类 git diff?) - ☐ Profile 在 XiLink 改了 link 后的失效提示策略(自动迁移 instanceId vs 强制重建)
- ☐ 第三方插件签名/license 校验(下半年 vendor registry 时再决议)
- ☐ auto_inverse 反向调音的输入数据格式(目标曲线 csv · 实测响应 wav)
7. 实施清单
7.1 本季度(P0 + P1)
- 起草
PROMPT-claudea-workspace-file-system.md(P0 · 7 后缀 + 项目目录 · 2.5d) - 起草
PROMPT-claudec-tuning-mode-system.md(P0 · 4 种 TuningMode + linkStore 双状态 · 2.5d) - 起草
PROMPT-claudeb-refresh-link-protocol.md(P0 · 后端 ClaudeB · 双语义 · 2d) - 起草
PROMPT-claudec-xipreset-xiprofile.md(P1 · ⭐ Preset+Profile 独立文件 · 2d) - 起草
PROMPT-claudec-tuning-mode-ui.md(P1 · XiTune 4 模式 UI · 2d) - 更新 KANBAN.md v6 · ClaudeA / ClaudeC / 后端 ClaudeB 任务表加 P0/P1 共 7 项
- 派发 P0 任务给 ClaudeA + ClaudeC + 后端 ClaudeB(可并行)
- P0 完成后 review · 全局回归测试
- 派发 P1 任务(依赖 P0)
- 本季度任务全部完成后 · 提案 status 和 ADR 实施清单更新
7.2 下季度(P2)
- 起草
PROMPT-claudea-thirdparty-module-scanner.md(P2 · 2d) - 起草
PROMPT-claudea-xiforge-menu-migration.md(P2 · 1d) - 视客户反馈决定是否启动云端 vendor registry
7.3 后续季度(P3)
- auto_inverse 反向调音占位(3d · 视算法预研)
- XiVST 云端 vendor registry(10d+ · 视商业化决策)
8. 关联文档
- 上游决议:
- ADR-AIOS-01(5 角色协作框架)
- ADR-AIOS-03(4 agent 按 stage 分工)
- ADR-AIOS-04(XiForge 架构 · ModuleDef UID)
- 触发提案:
XiStudio-Workspace-Proposal.md(v1.1 · status: decided) - 实施 PROMPT(本季度 · 待落盘):
PROMPT-claudea-workspace-file-system.mdPROMPT-claudec-tuning-mode-system.mdPROMPT-claudeb-refresh-link-protocol.mdPROMPT-claudec-xipreset-xiprofile.mdPROMPT-claudec-tuning-mode-ui.md- 相关代码真值调研:5 份 subagent 报告(2026-05-26 · commit
8e7071b)
ADR-AIOS-05 v1.0 · 2026-05-26 · accepted · 7 项决议拍板完成 · 等待 5 份 PROMPT 落盘后派发