跳转至
ACCEPTED

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 })   // ⚠️ 全发后端
}
→ 用户切到老 preset(字段少)· 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 *.xistudio manifest 协议 + 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 类语义服务

PresetProfileServicePresetReadService + 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 字段默认从 applypreview · 老前端调用 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 HandleSetAmbiancemode 字段(默认 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_DEPRECATEDhuman_readable_message: "请改用 .xistudio 格式" · 不提供 7 天宽限期
set_ambiance 不带 mode 字段 直接默认 preview(无 backward-compat fallback apply)· 老前端调用必须随 fork 8 同步改
current_pro 启动残留 启动直接清 + 写 .workspace_owner 标记 · 不弹 confirm dialog · 用户已知会丢"上次未保存"内容
profile_ 老 id 保留作内部主键 · 显示用 name(用户零感知)
老 e2e 测试用例 1-2 个依赖"save_preset 副带 apply_params"的用例直接改逻辑 · 不留兼容路径

9. Validation(验收标准)

9.1 4 个端到端 case(必过)

  1. Case 1 · 残留清理:打开新工程后 · data/current_pro/profiles/ + data/current_pro/presets/ 全空 · 前端 profile 列表 / preset 列表显示空 · .workspace_owner 文件存在
  2. Case 2 · save_preset 零副作用:用户点保存 preset · 后端 log 不出现 RebuildChain / SetMixerMatrixRow / LoadWavSource / apply_params / _tryApplySourceParamRealtimeAsync 任何一项
  3. Case 3 · profile preview vs apply:用户点 profile · 链路结构不变 · preset 值不变(_paramStore 不动)· 用户必须显式点"应用激活"按钮才下发 DSP
  4. 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.md v1.4 §ADR 生命周期 + §UID 命名规范 + §Plan/Act 切换矩阵 + §Ximind 兼容性铁律
  • .clinerules/aios-orchestration.md v1.8 §DASHBOARD 切片归档协议
  • .clinerules/xisound-docs-migration.md v2.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_workspace API 供大模型主动触发
  • 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_force API 有 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 不冲突