跳转至
ACCEPTED

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 耦合"

refresh_link 协议有 2 种语义:

  1. 加载时反读(target_live 模式专用):
  2. 打开 .xitune · 模式为 target_live → 自动调用 refresh_link 从 DSP 反读链路结构 → 填充 linkStore.deployed

  3. 校验/对账(pc_simulation + target_live 都可触发):

  4. 用户主动点 "刷新链路" 按钮 → refresh_link 反读 → 与 host 当前 linkStore 比较 → 显示差异(missing modules / param drift / connection mismatch)
  5. 用途:确认 DSP / PC backend 加载的 link 与 host 工程一致(对账机制)
  6. 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 业界做法不符

否决(用户澄清后修正)。 - 用户原话:"已固化在 dsp 中可读取链路结构 · 用来确认是否符合预期" - 校验/对账语义对 pc_simulation 模式也很重要(后端可能加载错 link) - 双语义实现成本相同 · 缺一会有功能缺失


4. 后果(Consequences)

4.1 正面后果

  1. 文件类型识别清晰:7 后缀都以 .xi 开头 · 视觉一致 · 用户能秒辨文件类型
  2. 项目可 git 管理:目录模式 + 文本 JSON · 可逐文件 diff
  3. 跨角色协作友好:算法工程师改 .xilink · 调音师改 .xitune/.xiprofile · 不冲突
  4. 4 种 TuningMode 是产品差异化:超越简单的"读/写 link"二元 · 直接对标 AWE Tuning Mode
  5. Preset 跨工程复用:类 VST3 模式 · 提高调音师效率
  6. 第三方插件生态可扩展:类 AWE 目录扫描 · 为下半年 vendor registry 留口
  7. refresh_link 校验:解决"DSP 加载的 link 与 host 工程不一致"低级 bug
  8. 本季度可落地:P0+P1 共 10d · 跨 4 agent 并行 6-8 天

4.2 负面后果

  1. 重构成本:workspaceStore + linkStore + 多个 dialog 都要改 · 需要充分回归测试
  2. 后端协调:refresh_link 协议必须前后端同步落地 · 跨 CLI 协调成本上升
  3. migration 兼容:现有 .json 文件需要迁移工具(自动检测旧格式 + 转换)· 1 周内可能有遗留文件
  4. Profile 双重校验逻辑复杂:linkRef + linkUid + instanceId 一致性校验 · 需要单元测试覆盖
  5. 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 · 仅留实施期可能浮现的子问题)

  1. ☐ 旧 .json workspace 迁移向导是否需要单独 sprint?(P-1 实施时核实)
  2. refresh_link 校验差异的 UI 设计(diff view · 类 git diff?)
  3. ☐ Profile 在 XiLink 改了 link 后的失效提示策略(自动迁移 instanceId vs 强制重建)
  4. ☐ 第三方插件签名/license 校验(下半年 vendor registry 时再决议)
  5. ☐ 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.md
  • PROMPT-claudec-tuning-mode-system.md
  • PROMPT-claudeb-refresh-link-protocol.md
  • PROMPT-claudec-xipreset-xiprofile.md
  • PROMPT-claudec-tuning-mode-ui.md
  • 相关代码真值调研:5 份 subagent 报告(2026-05-26 · commit 8e7071b)

ADR-AIOS-05 v1.0 · 2026-05-26 · accepted · 7 项决议拍板完成 · 等待 5 份 PROMPT 落盘后派发