ADR-AIOS-15 · Workspace 持久化协议规范化 + Profile/Preset 副作用切除
1. Context(背景)
1.1 触发事件
2026-06-03 15:46 用户在 xilink stage 实测发现 6 类持久化与 preset/profile 副作用问题 · 16:00 追加 2 类(save_preset 隐式触发链路重建 + Mixer 矩阵全 0)。Cline-AIOS 经 5 路 subagent 并行真值核查(2026-06-03 15:46~17:13)+ 直接 read_file 补完整代码 · 锁定全部 8 类 bug 的代码层精确根因(行号级)。
用户拍板(2026-06-03 17:18):
- 方向 B(外科切除) + ADR 编号 ADR-AIOS-15(13 已被 xitest-realtime-dual-mode 占用)
- fork 4 applyPresetLocally 默认值策略选 B(保留默认补全显示 · 但 apply_params payload 只发 preset 文件原值 · 避免污染 _paramStore)
- fork 6 mixer 矩阵还原选 Y(service 层负责还原 · 不动 ChainBuilder · 职责分离)
- legacy 兼容期 不需要 · 一次性彻底改(.xi 直接拒绝 / mode 字段直接默认 preview / current_pro 启动直接清)
1.2 用户原话(2026-06-03 15:46 · 16:00 追加)
"针对 xilink stage 的 workspace 持久化的流程: 1. 再打开后端和前端,没有做任何工程加载的情况下,现在会一直打印 FindLinkFile manifest-driven · 为什么会有这个 log,是否合理 · 并且打开之后就会加载奇怪的内容,音效模式中也会有参数,不知道是哪里的,感觉是 currentproj 中有残余内容被加载了 2. workspace 中数据整理 · 目前看到有 .xi .xistudio 到底使用的哪一个作为 workspace 的总工程 · *.bin 是作为工程序列化的最终 bin 文件么? 3. profile 和各个模块的 preset 名字为什么还是一串数字而不是 module 中定义的 preset 名字/profile 定义的名字? 4. 当前 module preset 中保存会将参数报错到所有当前 module 的 preset 中 5. 模式设定的逻辑 · 选中一个 profile 后 · 不应该直接下发参数 · 而是再选中的界面中添加激活,取消,类似你现在 active · 但是 active 目前有问题,会导致链路的 link 结构有变化,preset 的值变成空 6. 在 preset 的左边的重置添加一个保存按钮,没有 dirty 的时候也可以强制保存"
"1. 所有 Module 的 preset 起了一样的名字 · 还是 tttmixer 测试工程 · 我在点击保存 wav 的 preset 和直接点击 preset 后 · 整个链路的声音 preset 加载错乱了 · 按照道理保存动作应该不会有声音变化 · 点击 preset 也只是加载当前 module 的 preset · 声音也不会有变化 · 但是实际上应该是其他的 preset 也有变化了 · 尤其是 mixer 2. 当前 ttmixer 工程中 source group 子图中有四个 source 第 3,4 个分别是 wav 和 device · 这两路不能正常出声 · 一会是正弦波一会是 pink 但是都不可控"
1.3 真值核查发现(5 路 subagent + 直接 read_file)
第 1 路:FindLinkFile 调用链 + current_pro 拷贝流程
- 唯一 log 源头:
backend_csharp/Services/Workspace/WorkspaceFileHelper.cs:27 FindLinkFile - 高频调用方:
PerModuleMetricsCollector.cs:116(指标 tick 缓存过期重读)+AudioEnginePostStartSourceService.cs:81/145/234/293(PostStart 4 hooks)+AudioEngineChainBuilder.cs:64(链路重建) - ⚠️ 关键缺陷:
WorkspaceProjectService.HandleOpenProject:105直接从 manifest 取 projectId · 不清 current_pro 旧文件 → 上次工程tttmixer.xilink/*.preset.json/*.profile.json残留全部被识别为有效工程加载
第 2 路:文件类型职责
- ✅
*.xistudio= workspace 顶层工程清单 ·WorkspaceFileHelper.cs:13 ManifestExt = ".xistudio"· JSON manifest · 含schemaVersion=aios-05+files{link,presets[],profiles[],tests[]} - ❌
*.xi= legacy 残留(grep 无活跃使用)· 应直接拒绝 - ✅
*.bin= DSP 引擎二进制序列化产物(参数快照/状态机 dump)· 不属于工程主载体
第 3 路:profile/preset id 命名
- 唯一生成点(纯前端 timestamp):
frontend_vue3/src/stages/xitune/ProfileSidebar.vue:458·profileId = \profile_${Date.now()}``frontend_vue3/src/stages/xitune/PresetPanel.vue:477·presetId = \\({instanceId}_\)``- 后端 HandleSavePreset/HandleSaveProfile 完全被动接收 ·
name字段已存在 · 但前端列表显示用 id 不用 name(显示 bug 不是数据缺失)
第 4 路:HandleSavePreset 完整代码(PresetProfileService.cs:46-83)
_paramStore类型:ConcurrentDictionary<string, string>· key 模式{instanceId}.{paramName}(不含 presetId)- 落盘:
{instanceId}/{presetId}.preset.json单文件 · presetId 隔离 ✅ - HandleListPresets(
:132-160)完全从文件读 · 不碰 _paramStore ✅ - → 后端 preset 文件之间无污染
第 5 路:HandleSetAmbiance 完整代码(PresetProfileService.cs:306-347)
- 用户点 profile → 前端发
set_ambiance→ 后端 4 件副作用同时干: - 1️⃣ 读 profile 文件
- 2️⃣ 写入
_paramStore[$"{instanceId}.{paramName}"] = value(覆盖内存) - 3️⃣
TryFlushToDspAsync(异步未 await · 下发 DSP) - 4️⃣ source 模块:
_tryApplySourceParamRealtimeAsync→ 触发 RebuildChain - ⚠️ 隐式自动调用(用户没主动激活也会调):
Services/Link/LinkParamApplyService.cs:346(apply_link 完成后)Services/Project/ProjectService.cs:249(LoadProjectAsync)- 注释标 "DQ6 选项 A" · 是历史遗留设计
直接 read 前端代码(PresetPanel.vue:240-500)
真凶锁定 - 不是后端污染 · 是前端 applyPresetLocally:294-304 的兜底逻辑:
function applyPresetLocally(storedParams) {
const fullParams = { ...getModuleDefaults(), ...storedParams } // ⚠️ 默认值打底 + preset 覆盖
for (const [paramId, value] of ...) linkStore.setParamValue(...)
send({ type: 'apply_params', instanceId, params: fullParams }) // ⚠️ 全发后端
}
getModuleDefaults 补全为 ~10 字段 · apply_params 写入 _paramStore 多余 keys → 下次操作 mixer 时 HandleApplyParams:198-208 遍历 mixSrcCount# 调 FlushMixerRowToDsp · _paramStore 中没有用户保存的 mixer 矩阵 keys → DSP 拿到全 0 矩阵 → log 见 SetMixerMatrixRow gain=[0,0,0,0]
→ 用户感受:① 切其他 preset 拿到的不是 preset 设定值(getModuleDefaults 补的) ② 保存后链路全乱(apply_params 触发 RebuildChain · mixer 全 0)
2. Problem(问题陈述)
xilink stage 的 workspace 持久化层 + preset/profile 状态机存在多处副作用耦合:后端 preset 文件 / paramStore / DSP 链路三层数据通路缺乏边界;前端 applyPresetLocally 兜底逻辑放大 paramStore 失同步问题;两处隐式 HandleSetAmbiance 调用让用户在打开工程 / 加载 link 等基本操作中触发链路重建 + Mixer 矩阵覆写。系统行为不可预测 · 用户基本操作(打开工程 / 保存 preset / 选 profile)产生超预期副作用。
3. Constraints(硬约束)
3.1 不可变契约
- ADR-AIOS-05
*.xistudiomanifest 协议 + 7-suffix 文件家族不变 - WS message type 名称(
save_preset/set_ambiance/apply_params等)保留 · payload 字段可扩 - DSP 引擎 API(
RebuildChain/SetMixerMatrixRow/LoadWavSource)契约不动 · 调用方负责正确性
3.2 ⭐ Ximind 大模型兼容性铁律(.clinerules v1.3)
所有 8 fork 必须满足 5 项可读 / 可写 / 可解释 / 可追溯 / 可恢复:
- ✅ 状态可读(7 endpoint):
list_profiles/list_presets/get_active_profile/get_workspace_info/get_module_params/get_link_topology/get_audit_log— 全部 JSON 自描述 + 含description/humanReadableName字段 - ✅ 操作可写(8 endpoint):
clean_workspace/activate_profile(mode: preview|apply)/save_preset_force/apply_profile/deactivate_profile/set_param_with_intent/rebuild_chain_explicit/preset_search - ✅ 意图可解释:每个 ack 含
{success, code, human_readable_message, recovery_hints[]} - ✅ 路径可追溯:current_pro 切换 / preset 保存 / profile 激活全部走审计日志(WS 推 +
data/current_pro/.audit/YYYY-MM-DD.jsonl持久化) - ✅ 错误可恢复:错误 ack 含
recovery_hints数组(action+label · eg.force_save/discard/save_as_new)
3.3 落地检查清单(每 fork 必含)
- 已识别 N 个"Ximind 可读状态"
- 已识别 M 个"Ximind 可写操作"
- 已设计审计日志路径
- 已定义 error 结构(code / message / recovery_hints)
- 业务流程关键 Step 含
description字段
4. Options(方向 A/B/C)
方向 A:大手术 · 重构 4 类语义服务
拆 PresetProfileService 成 PresetReadService + PresetWriteService + ProfileActivateService + ChainApplyService 4 服务。
Trade-off:✅ 彻底解耦 / ❌ 工作量 5-7 天 / ❌ 跨 ClaudeA + ClaudeB 大重构 / ❌ 现有 e2e 大改
方向 B:外科切除 · 8 fork 精确修复 ⭐ 用户拍板
基于 5 路精核证据 · 每个 fork 修复点已到具体行号。
Trade-off:✅ 工作量 ~2.5 天关键路径 / ✅ 不破坏现有架构 / ✅ 可分 4 Phase 并行 / ⚠️ 副作用切除可能漏点(由验收 4 case 兜底)
方向 C:补丁式 · 只动前端
不解决后端隐式副作用 + Mixer 矩阵 bug · ❌ 治标不治本
5. Decision(决议)
用户拍板方向 B(外科切除) · 2026-06-03 17:18:
"1.同意 2 同意 3B 4.Y 5.legacy 不需要兼容期,直接一次性彻底改好"
具体策略:
- ADR 编号 ADR-AIOS-15(13 已被占用)
- 8 fork 分 4 Phase · 总工期 ~2.5 天关键路径
- fork 4 选 B:applyPresetLocally 保留 getModuleDefaults 补全做 UI 显示 · 但 apply_params payload 只发 preset 文件原值(不污染 _paramStore)
- fork 6 选 Y:RebuildChain 完成后由 service 层(PresetProfileService 或新增 ChainStateRestoreService)从 _paramStore 还原 mixer 矩阵 · ChainBuilder 不动
- ⭐ 无 legacy 兼容期:.xi 扩展名直接拒绝加载 / set_ambiance 不带 mode 字段时直接默认 preview / 启动直接清 current_pro 残留(不要 confirm dialog · 一次性彻底)
6. Consequences(影响面)
6.1 正面
- "保存动作无副作用" / "选 profile 不下发 DSP" / "打开工程不带残留" 三条铁律落地
- 后端日志降噪:启动到首次 WS 连接日志 ≤ 50 行(目前 200+)
- Ximind 大模型 7 读 + 8 写 endpoint 完整暴露 · 可主动调用
- mixer 矩阵 bug 修复 · source_wav / source_device 出声可控
6.2 负面
- 现有依赖"save_preset 副带 apply_params 让链路立即生效"的 e2e 测试需调整(估 1-2 用例)
- HandleOpenProject 启动直接清 current_pro · 可能破坏"上次未保存退出后下次想恢复"使用习惯 · 用户拍板接受(无兼容期)
mode字段默认从apply→preview· 老前端调用set_ambiance不带mode时不再下发 DSP · 必须随 fork 8 同步前端 UI 改动(否则用户感觉激活无效)
6.3 跨进程影响
- ⚠️ ADR-AIOS-08 xilink-stage-ux:profile UX 改动需协调(fork 8 与 ADR-08 议题 ⑤ 右 dock 裁剪有 UI 相邻)
- ⚠️ ADR-AIOS-05 xistudio-workspace:current_pro 协议补
.workspace_owner标记文件 - ✅ ADR-AIOS-13 xitest-realtime-dual-mode:无影响(实时链路不依赖 _paramStore)
- ✅ ADR-AIOS-16 subgraph-unified-architecture:无影响(子图扁平化不动 preset 文件)
7. Implementation(8 fork U-thread · 修复点已到行号)
7.1 fork 总表
| # | UID | 标题 | 部门 | 隔离 | 工作量 | 修复点(精确到行) | Ximind |
|---|---|---|---|---|---|---|---|
| 1 | P5.UA15-workspace-clean-protocol | open/new project 启动直接清 current_pro · 加 .workspace_owner 标记 |
后端 ClaudeB | 🧵 file | 0.5d | WorkspaceProjectService.HandleOpenProject:105 加 hard-clean current_pro + 写 .workspace_owner 文件 |
✅ |
| 2 | P5.UA15-findlinkfile-cache | FindLinkFile 加 cache + 日志 lazy 打 | 后端 ClaudeB | 🧵 file | 0.3d | WorkspaceFileHelper.cs:27 FindLinkFile 加 cache(key=projectDir + manifest mtime)· PerModuleMetricsCollector.cs:116 移除 lazy 重读 |
— |
| 3 | P5.UA15-implicit-setambiance-切除 ⭐ | 删两处隐式自动调用 HandleSetAmbiance(关键路径) | 后端 ClaudeB | 🧵 file | 0.5d | 删 LinkParamApplyService.cs:346 + ProjectService.cs:249 注释 "DQ6 选项 A" 的两处 await HandleSetAmbiance(fakeRoot, null) |
✅ |
| 4 | P5.UA15-applypreset-defaults-策略B | applyPresetLocally 保留 UI 默认补全 · apply_params payload 改发 preset 原值 | 前端 ClaudeA | 🧵 file | 0.5d | PresetPanel.vue:294-304 applyPresetLocally 行 297-302 拆两份:uiParams = { ...defaults, ...storedParams } 仅 setParamValue · apiParams = { ...storedParams } 仅发 apply_params |
✅ |
| 5 | P5.UA15-active-preview-apply | HandleSetAmbiance 拆 preview/apply 双段 · 默认 preview | 后端 ClaudeB | 🧵 file | 0.8d | PresetProfileService.cs:306-347 HandleSetAmbiance 加 mode 字段(默认 preview)· preview 只读 profile 文件并返回数据(不写 _paramStore 不下发 DSP)· apply 才走原 4 件副作用 |
✅ |
| 6 | P5.UA15-mixer-matrix-restore-service | service 层 RebuildChain 完成后还原 mixer 矩阵 | 后端+DSP ClaudeB | 🚀 task | 1d | 新增 Services/AudioEngine/ChainStateRestoreService.cs(或 PresetProfileService 加方法)· hook RebuildChain 完成事件 · 遍历 _paramStore 含 {iid}.mixSrcCount#N 的 keys 调 FlushMixerRowToDsp 还原 |
— |
| 7 | P0.UA15-preset-display-name | 前端列表显示 name 优先 | 前端 ClaudeA | 🧵 file | 0.3d | PresetPanel.vue + ProfileSidebar.vue 列表渲染处 <option> 改 {{ p.name || p.presetId }} · 内部主键仍用 id |
✅ |
| 8 | P0.UA15-active-ui-decouple-buttons | 前端 profile 选中只切 UI · 加"应用激活"+"强制保存"按钮 | 前端 ClaudeA | 🧵 file | 0.5d | ProfileSidebar.vue 加"应用激活"按钮触发 set_ambiance(mode: 'apply') · PresetPanel.vue:459 saveActivePreset 加 always-enabled "强制保存"按钮(:disabled="false") |
✅ |
7.2 Phase 划分
- Phase 1(并行 0.5d):fork 1 + fork 2 + fork 7(三条独立路径 · 立即派 · 文件正交)
- Phase 2(关键路径 0.5d):fork 3(切隐式副作用 · 解锁 ⅘/6)
- Phase 3(并行 1d):fork 4 + fork 5 + fork 6(依赖 fork 3 zombie)
- Phase 4(收尾 0.5d):fork 8(依赖 fork 5 的
mode字段就绪)
总工期:~2.5 天关键路径
7.3 文件正交核查
| 文件 | 涉及 fork | 串行 vs 并行 |
|---|---|---|
PresetProfileService.cs |
fork 5 | fork 5 独占(fork 3 不改此文件) |
LinkParamApplyService.cs |
fork 3 | fork 3 独占 |
ProjectService.cs |
fork 3 + fork 1 | ⚠️ 串行(fork 1 改 HandleOpenProject:105 · fork 3 改 :249 · 行不重叠但同文件)· 推荐 fork 1 zombie 后再起 fork 3 |
WorkspaceFileHelper.cs |
fork 2 | fork 2 独占 |
PerModuleMetricsCollector.cs |
fork 2 | fork 2 独占 |
PresetPanel.vue |
fork 4 + fork 7 + fork 8 | ⚠️ 串行(三 fork 都改此文件)· 顺序:fork 4 → fork 7 → fork 8 |
ProfileSidebar.vue |
fork 7 + fork 8 | ⚠️ 串行(fork 7 → fork 8) |
Services/AudioEngine/ChainStateRestoreService.cs(新增) |
fork 6 | fork 6 独占 · 🚀 task 隔离 worktree |
7.4 K-thread 拓扑
- ClaudeB(后端主战场):fork 1 / 2 / 3 / 5 / 6(5 个后端 fork · 其中 fork 6 独立 worktree)
- ClaudeA(前端副战场):fork 4 / 7 / 8(3 个前端 fork · 同 worktree 串行)
- ⚠️ ClaudeA 3 fork 文件重叠(都改 PresetPanel.vue / ProfileSidebar.vue)· 必须串行
- ✅ ClaudeB 5 fork 中 ⅓ 都改 ProjectService.cs 但行不重叠(105 vs 249)· 串行更安全
8. Migration(legacy 处理 · 无兼容期)
⭐ 用户拍板:不要 legacy 兼容期 · 一次性彻底改
| 项 | 处理方式 |
|---|---|
.xi 扩展名 |
启动直接拒绝加载(error_code: LEGACY_EXTENSION_DEPRECATED)· human_readable_message: "请改用 .xistudio 格式" · 不提供 7 天宽限期 |
set_ambiance 不带 mode 字段 |
直接默认 preview(无 backward-compat fallback apply)· 老前端调用必须随 fork 8 同步改 |
| current_pro 启动残留 | 启动直接清 + 写 .workspace_owner 标记 · 不弹 confirm dialog · 用户已知会丢"上次未保存"内容 |
| profile_ |
保留作内部主键 · 显示用 name(用户零感知) |
| 老 e2e 测试用例 | 1-2 个依赖"save_preset 副带 apply_params"的用例直接改逻辑 · 不留兼容路径 |
9. Validation(验收标准)
9.1 4 个端到端 case(必过)
- ✅ Case 1 · 残留清理:打开新工程后 ·
data/current_pro/profiles/+data/current_pro/presets/全空 · 前端 profile 列表 / preset 列表显示空 ·.workspace_owner文件存在 - ✅ Case 2 · save_preset 零副作用:用户点保存 preset · 后端 log 不出现
RebuildChain/SetMixerMatrixRow/LoadWavSource/apply_params/_tryApplySourceParamRealtimeAsync任何一项 - ✅ Case 3 · profile preview vs apply:用户点 profile · 链路结构不变 · preset 值不变(_paramStore 不动)· 用户必须显式点"应用激活"按钮才下发 DSP
- ✅ Case 4 · mixer 矩阵恢复:tttmixer 工程 source_wav + source_device 第 ¾ 路出声可控 · 链路重建后 mixer 矩阵从 _paramStore 还原非全 0 · 不再走 sine/pink fallback
9.2 测试基线
- 前端 e2e:
work-cline/e2e/+work-cline/integrationtest/100% 通过(允许 1-2 用例改逻辑后 100%) - 后端单测:
backend_csharp/test/100% 通过 - 后端启动日志:启动到首次 WS 连接 ≤ 50 行(降噪 4x · 当前 ~200 行)
9.3 大模型可调 API 验证(Ximind)
- POST
/api/workspace/clean大模型可触发 · 返回{success, cleanedItems[], recovery_hints[]} - WS
set_ambiance(mode: "preview")大模型可"试听不应用" · 不动 _paramStore 不下发 DSP - 所有 ack 含
human_readable_message字段(中文 · 自然语言) data/current_pro/.audit/2026-06-03.jsonl文件存在 · 含本次会话所有操作记录
10. References
10.1 协议依据
.clinerules/aios-orchestration.mdv1.4 §ADR 生命周期 + §UID 命名规范 + §Plan/Act 切换矩阵 + §Ximind 兼容性铁律.clinerules/aios-orchestration.mdv1.8 §DASHBOARD 切片归档协议.clinerules/xisound-docs-migration.mdv2.0 §工程代码位置铁律(Cline 不写业务代码)
10.2 关联 ADR
ADR-AIOS-05-xistudio-workspace.md(7-suffix manifest 协议 · 不变)ADR-AIOS-08-xilink-stage-ux.md(profile UX · fork 8 受影响 · 需协调 right dock UI)ADR-AIOS-12-xitest-realtime-arch.md(实时链路 · 无影响)ADR-AIOS-13-xitest-realtime-dual-mode.md(双模数据链路 · 无影响)ADR-AIOS-16-subgraph-unified-architecture.md(子图扁平化 · 无影响)
10.3 真值核查证据(2026-06-03 15:46~17:13 · 5 路 subagent)
- 第 1 路:FindLinkFile 调用链 →
WorkspaceFileHelper.cs:27+ 7 处高频调用方 - 第 2 路:文件类型 →
*.xistudio=WorkspaceFileHelper.cs:13 ManifestExt(权威) - 第 3 路:id 命名 →
ProfileSidebar.vue:458+PresetPanel.vue:477纯 timestamp - 第 4 路:HandleSavePreset(
PresetProfileService.cs:46-83)+ HandleListPresets(:132-160)代码原文 - 第 5 路:HandleSetAmbiance(
PresetProfileService.cs:306-347)+ 两处隐式调用(LinkParamApplyService.cs:346+ProjectService.cs:249) - 直接 read:
PresetPanel.vue:240-500(applyPresetLocally / preset_list 处理 / saveActivePreset / createPreset / getModuleDefaults)
10.4 用户实测 log 证据链(2026-06-03 16:00)
← save_preset {presetId: "source_wav_v1#1_1780368501180", params: {...}}
→ preset_save_ack {success: true}
← list_presets (前端自动跑)
→ preset_list (返回的 preset 列表)
← apply_params {instanceId: "source_wav_v1#1", params: {...}} ⚠️ 谁触发的?(已锁定:applyPresetLocally:302)
[AudioEngine] 音调信号源切换 waveform=sine freq=440~440Hz ⚠️ source 模块被重置
[LinkFrameBuilder] 帧构建完成: 9 个 DSP 模块 ⚠️ RebuildChain 触发
[AudioEngine] SetMixerMatrixRow gain=[0,0,0,0] ⚠️ Mixer 矩阵全 0(_paramStore 没存)
[DSP/LoadWavSource] WAV loaded OK ⚠️ wav 模块被重新加载
11. ⭐ Ximind Compatibility(.clinerules v1.3 铁律)
11.1 大模型可读状态(7 endpoint)
| Endpoint | JSON 自描述字段 | 大模型用途 |
|---|---|---|
WS list_profiles |
[{profileId, name, description, modulePresets, createdAt, lastUsedAt}] |
"用户最近用过哪些声音模式" |
WS list_presets |
[{presetId, name, description, params, isDefault, isUserCreated}] |
"推荐用户切到 preset X" |
WS get_active_profile |
{profileId, name, isPreviewMode, isApplyMode, dirtyModules[]} |
"当前哪个 profile 在激活" |
WS get_workspace_info |
{projectName, manifestPath, lastSavedAt, hasUnsaved, owner} |
"当前工程是否需要保存" |
WS get_module_params |
{instanceId, params: {paramName: {value, displayName, unit, range}}} |
"解释当前 mixer 增益是多少 dB" |
WS get_link_topology |
{sources[], modules[], connections[], description: "..."} |
"解释音频链路结构" |
WS get_audit_log |
[{timestamp, operation, before, after, reason}] |
"回放最近 10 个用户操作" |
11.2 大模型可写操作(8 endpoint)
| Endpoint | Payload 自然语言字段 | 大模型用途 |
|---|---|---|
WS clean_workspace |
{reason: "user requested fresh start", force: false} |
"清空当前工程残留数据" |
WS activate_profile |
{profileId, mode: "preview" \| "apply", description: "试听 X 模式"} |
"试听某个声音模式不下发" |
WS save_preset_force |
{instanceId, presetId, params, description: "用户手动强制保存"} |
"无 dirty 也强制保存" |
WS apply_profile |
{profileId, description: "用户确认应用模式"} |
"正式应用激活" |
WS deactivate_profile |
{description: "用户取消激活返回默认"} |
"取消激活回默认" |
WS set_param_with_intent |
{instanceId, paramId, value, intent: "用户调暗低音"} |
"带意图的参数调整" |
WS rebuild_chain_explicit |
{reason: "manual reload after device change"} |
"明确触发链路重建(诊断用)" |
WS preset_search |
{query: "找出包含'重低音'的所有 preset"} |
"自然语言搜索 preset" |
11.3 自然语言描述字段(必填)
所有 WS message ack 必含:
{
"type": "...",
"success": true | false,
"code": "OK" | "PRESET_NOT_FOUND" | "WORKSPACE_DIRTY" | "LEGACY_EXTENSION_DEPRECATED" | ...,
"human_readable_message": "已激活'重低音'模式 · 7 个模块参数已切换",
"recovery_hints": [
{"action": "force_save", "label": "强制保存覆盖"},
{"action": "discard", "label": "放弃改动"}
]
}
11.4 审计日志路径
- WS 推:
workspace_audit_event实时推送给所有连接 - 持久化:
data/current_pro/.audit/YYYY-MM-DD.jsonl(每日一个 jsonl 文件) - 大模型可通过
WS get_audit_log读最近 N 条
11.5 Error 结构铁律
{
"success": false,
"code": "PRESET_DIRTY",
"message": "preset 'testA' 有未保存改动",
"human_readable_message": "Preset 'testA' 有未保存的修改 · 是否覆盖?",
"recovery_hints": [
{"action": "force_save", "label": "强制保存覆盖"},
{"action": "discard", "label": "放弃改动"},
{"action": "save_as_new", "label": "另存为新 preset"}
]
}
11.6 服务 Ximind 兼容性的 fork 标注
- ✅ fork 1(workspace-clean-protocol):暴露
WS clean_workspaceAPI 供大模型主动触发 - ✅ fork 3(implicit-setambiance-切除):移除隐式副作用让大模型行为可预测
- ✅ fork 4(applypreset-defaults-策略B):不污染 _paramStore · 大模型 read 状态准确
- ✅ fork 5(active-preview-apply):
mode: "preview"让大模型试听 - ✅ fork 7(preset-display-name):大模型读 name 而非 id
- ✅ fork 8(active-ui-decouple-buttons):
save_preset_forceAPI 有 UI 对应
本 ADR proposed 时间:2026-06-03 17:30 本 ADR accepted 时间:2026-06-03 17:55(用户
accept ADR-AIOS-15· v1.1 升版) 下一步:Phase 1 三 fork(F1 workspace-clean-protocol + F2 findlinkfile-cache + F7 preset-display-name)立即派发到 prompts/active/ · ClaudeB ×2 + ClaudeA ×1 文件正交并行 · 与现有 H2/F8 hotfix 不冲突