ACCEPTED
状态:✅ v0.1 accepted · 2026-06-18 16:50(用户三轮拍板 12:30 双链路矩阵 + 15:45 log_module 修正 + xitest/xitune 延展 + 16:46 框图 HTML 通过 + 启动 ADR · v3.0 直接 accepted 起步)
框图基线:docs/02-products/P1-xistudio/Meter-Architecture-Framework.html v0.2(5 张 Mermaid 图 + 4 个 sequence + 4 张表 · 本 ADR §3 业务契约 5 必填段引用此基线)
关键定位:本 ADR 不写代码 · 不动子仓污染(用户已自处理)· 仅作为业务行为契约 + 实施清单 fork 表派发基线
§1 背景与动机
§1.1 触发链(用户 verbatim · 2026-06-18 三轮拍板)
关于你关键启示我有几个和你理解不一致的点
1.PC模式下的source和sink是可以作为节点直接fork给sidecar的,但是这只是满足了看输入何输出的需求,而链路内的节点还是需要meter探针来抓取的
2.和1是一样的原因
3.现在到底走sidecar还是pc,完全是根据具体的场景,如果是链路中的meter module,那肯定是走C实时的,如果是右侧dock中大部分应该走的sidecar,右侧可观察的节点只有source,sink,输入输出设备,logmodule,如果是PC模式,这些都是音频文件可获取;如果是DSP模式那source和sink就不可获取,那右侧就智能显示logmodule和输入输出设备(硬件)
4.归结到这里之后基本功能路径明确了,我们还需要有一个前提就是DSPruntime的路径选择,目前再引擎中有一个runtime target的设定,但是目前一致都是加载中的状态,这个引擎选择需要做如下修改
a)将xilink右侧的第一个dock指标监控挪到左侧dock改名为资源监控,并将runtime target挪到这个dock中,因为都属于硬件资源统计
b)target 的类型应该有连接类型来决定,在左侧dock连接中底层DSP通信中添加一个native的选项,如果是native代表是CPU 模式;
c)如果连接是native模式,那么资源监控的target就需要改为:CPU0 CPU1 CPU2 CPU3 VDSP0~VDSP3 VDSP表明是虚拟DSP模式,这种模式和CPU模式的区别就是source到底走的是DSP还是CPU;
第 2 轮 15:45 · log_module 修正 + xitest/xitune 延展
关于交互C 双击module出来的是音频log数据类型的落盘选择,并不会显示具体的界面;这个module主要的功能就是音频log或者文字log的落盘
右侧dock不会有专门的log moudle 而是在不同的dock中可以追踪link中的所有log module,来在对应的dock中显示这个log module的wav文件,来进行rms或者fft等显示
说到这里我们需要再延展以下到xitest里面的实时模式,实时模式中所有主界面基本是属于设备和source sink相关的都应该走的是pysidecar;xitune中的meter显示相关的和xilink几乎一致
第 3 轮 16:46 · 启动 ADR
启动ADR-AIOS-26-meter-dual-link-runtime-target-tri-stage.md
§1.2 verbatim 三轮关键定位拆解
| 轮 |
用户原话片段 |
真意定位 |
决议章节 |
| 1.1 |
"链路内的节点还是需要 meter 探针来抓取" |
链路中间 meter 探针永远走链路 1(dspalgo C) |
§3.2 |
| 1.2 |
"右侧 dock 中大部分应该走的 sidecar" |
右侧 Dock 走链路 2(pysidecar) |
§3.3 |
| 1.3 |
"DSP 模式那 source 和 sink 就不可获取" |
DSP 模式 source/sink 节点隐藏 |
§3.3 ② |
| 1.4 |
"将 xilink 右侧的第一个 dock 指标监控挪到左侧 dock 改名为资源监控" |
XiLink 左侧 Dock 改造:资源监控 dock 入驻 |
§3.1 ① |
| 1.5 |
"底层 DSP 通信中添加一个 native 的选项" |
连接 Dock 新增 native 连接类型 |
§3.1 ② |
| 1.6 |
"CPU0 CPU1 CPU2 CPU3 VDSP0~VDSP3" |
native 模式资源监控 target 池枚举 |
§3.1 ③ |
| 2.1 |
"双击 module 出来的是音频 log 数据类型的落盘选择 · 并不会显示具体的界面" |
log_module 双击 = 落盘类型选择 Dialog · 不展示分析 UI |
§3.5 |
| 2.2 |
"在不同的 dock 中可以追踪 link 中的所有 log module · 来在对应的 dock 中显示这个 log module 的 wav 文件" |
log_module 是 Meter Dock 内部横切追踪能力 |
§3.6 |
| 2.3 |
"xitest 里面的实时模式中所有主界面基本是属于设备和 source / sink 相关的都应该走的是 pysidecar" |
XiTest 实时模式主路径 = 链路 2 |
§3.4 |
| 2.4 |
"xitune 中的 meter 显示相关的和 xilink 几乎一致" |
XiTune meter 复用 XiLink 双链路 + Meter Dock + 节点矩阵 |
§3.7 |
§1.3 现有组件清单(v3.0 铁律 · ADR-23→25→25-R1 三连教训承接 · grep 真值锚定)
铁律:用户原话名词必须 grep 找具体文件+行号 · "完善他"指代词必须解析为现有组件 · 标"完善 vs 新建"。
| # |
用户原话名词 |
现有组件 grep 真值路径 + 行号 |
处置 |
| 1 |
"RuntimeTarget" |
04_development/backend_csharp/Services/Runtime/IRuntimeTargetService.cs L21-27(RuntimeKind enum)+ RuntimeTargetRegistry.cs L15/25/35(三注册:PcNative0/PcNativeLegacy/DspHardware)+ AudioEngineService.cs:120(DSP/PC 分支) |
完善(加 VDSP 子类型 · 加 native 连接驱动) |
| 2 |
"右侧第一个 dock 指标监控" |
XiLink 右侧 Dock 现状(用户原话 · 待 F1 派发前 grep 确认具体 drawer 文件名 · 推测 04_development/frontend_vue3/src/stages/xilink/drawers/RightDock*Metrics*.vue) |
迁移(右 → 左 + 改名"资源监控") |
| 3 |
"左侧 dock 连接" |
XiLink 左侧 Dock 连接项(用户原话 · 待 F1 派发前 grep 确认 · 推测 LeftDock*Connection*.vue 或 ConnectionDock.vue) |
完善(加 native 选项) |
| 4 |
"链路 1 dspalgo C 实时探针" |
dsp_algo/dll/dspalgo_dll.h:442("Analysis Module Exports (ADR-18 §3.3 · DSP probe polling API)")+ modules/analysis/{rms,fft,scope,phase,transfer}_module.{c,h} typeId 0x100E0001~5 + backend_csharp/Native/AnalysisModuleNative.cs + Services/Meter/MeterTapService.cs + Routes/RealtimeWsRoutes.cs + frontend_vue3/src/composables/useDockMeterFrame.ts |
不动(ADR-18 + ADR-21 已落地 · 本 ADR 仅锚定路径) |
| 5 |
"链路 2 pysidecar" |
pysidecar/main.py(702 行 FastAPI · 30+ 端点)+ analyzer/{freq_response, rms, transfer_function, coherence, phase_tools, ...}.py(全 numpy/scipy · 完全不加载 dspalgo.dll) |
不动(ADR-12 + ADR-22 已落地 · 本 ADR 仅锚定路径) |
| 6 |
"log_module" |
XiLink 链路中 log_module 节点(用户原话 · 待 F2 派发前 grep 确认 · 推测 frontend_vue3/src/stages/xilink/nodes/LogModule*.vue + 对应 dsp_algo/modules/log_module/* 或 backend log writer 服务) |
完善(双击行为改落盘选择 Dialog) |
| 7 |
"xitest 实时模式主界面" |
frontend_vue3/src/stages/xitest/ 实时模式 stage + test-aux/meter/{RMSMeter, SpectrumChart, PhaseChart, WaveformChart, FreqResponseChart}.vue + composables/useMeterWs.ts |
路径锚定(主路径走链路 2 · 不变更代码) |
| 8 |
"xitune meter" |
XiTune stage 现有 meter 组件(F1 派发前 grep · 推测 stages/xitune/ 下复用 xilink Drawer*) |
路径锚定(复用 XiLink 双链路) |
派发前 worker 必跑:每个 fork prompt 的 [参考文档] 段必须含 grep 真值路径+行号 · 不允许凭文件名推测起草。
§1.4 v3.0 教训承接
ADR-23→25→25-R1 三连教训(用户原话名词必须 grep 找具体文件+行号 · "完善"指代词必须解析为现有组件)在本 ADR §1.3 已严格执行。本 ADR 起草过程经历:
- 4 路并行 subagent 调查(dspalgo dll API · pysidecar · backend_csharp · frontend_vue3)
- 1 路 subagent 验证 RuntimeKind enum 真实存在
- 框图先行(HTML v0.2 用户已确认)再起 ADR(避免空转 · 用户原话"现在都是在空转")
§2 决议(Decision)
§2.1 双链路并存(锚定不变)
| 链路 |
适用场景 |
实现栈 |
本 ADR 处置 |
| 链路 1 dspalgo C 实时探针 |
链路中间 meter 探针(用户拖入链路的 fft/rms/phase/scope/transfer module) |
dsp_algo/modules/analysis/*_module.c typeId 0x100E0001~5 + DLL polling API + Native/AnalysisModuleNative.cs + Services/Meter/MeterTapService.cs + WS frame stream + useDockMeterFrame.ts |
锚定 · 不变更 |
| 链路 2 pysidecar Python 离线/准实时 |
右侧 Dock 4 类节点 + Meter Dock 追踪 log_module + 设备级 + xitune auto_tune + xitest 实时模式主路径 |
pysidecar/main.py FastAPI + analyzer/*.py numpy/scipy + HTTP REST 30+ endpoints + popup 系列 + xitest meter 组件 |
锚定 · 不变更 |
① 指标监控 → 资源监控(左移 + 改名 · 用户 1.4)
| 项 |
ADR-21 / ADR-21-R1 现状 |
本 ADR 改造 |
| Dock 名称 |
右侧"指标监控" |
"资源监控" |
| Dock 位置 |
右侧 Dock 第 1 项 |
左侧 Dock(具体次序由 F1-1 派发时实施 worker 决定 · 优先紧邻"连接 Dock") |
| Dock 内容 |
指标列表(老内容 · 由 F1-1 worker grep 确认范围 · 整体迁移) |
指标列表 + RuntimeTarget 子面板(新增) |
② 连接类型新增 native 选项(用户 1.5)
| 现状(枚举推测) |
本 ADR 决议 |
| 连接 Dock → 底层 DSP 通信 → [serial / tcp / usb / simulator / ... ] |
追加 native 选项 · 选 native 即触发"CPU 模式"分支 |
// IRuntimeTargetService.cs / 或新增 RuntimeTargetSubKind
type NativeRuntimeTarget = 'CPU0' | 'CPU1' | 'CPU2' | 'CPU3'
| 'VDSP0' | 'VDSP1' | 'VDSP2' | 'VDSP3'
// VDSP 语义:虚拟 DSP 模式 · 与纯 CPU 的区别 = source 信号链是否经过 DSP 算法栈
// - CPU0~CPU3: source 走纯 CPU 合成(SourceSignalService.cs PcNative 路径)
// - VDSP0~VDSP3: source 走 DSP 算法栈(虚拟 DSP · 用 dspalgo.dll 在 PC 模拟 DSP runtime)
| RuntimeKind |
子类型 |
source 信号路径 |
资源监控 target 池显示 |
PcNative |
CPU0~CPU3 |
CPU 合成(SourceSignalService.cs 现状) |
CPU0 / CPU1 / CPU2 / CPU3 |
PcNative |
VDSP0~VDSP3 |
DSP 算法栈(虚拟 DSP) |
VDSP0 / VDSP1 / VDSP2 / VDSP3 |
DspHardware |
(现状不变) |
真实 DSP 硬件 core |
真实 DSP target |
PcSimulator |
(现状不变) |
simulator slot |
simulator target |
§2.3 三 Stage 统一对照(本 ADR 锚定)
| 维度 |
XiLink |
XiTune |
XiTest 实时模式 |
| 主用途 |
链路设计 + 调试 |
自动调音 + 目标曲线对比 |
端到端验证 + 设备级真值 |
| 链路 1 占比 |
高(链路内中间探针) |
中(复用 XiLink 探针) |
低(仅当用户主动插 meter module) |
| 链路 2 占比 |
中(右侧 Dock + log_module) |
中(同 XiLink + auto_tune HTTP) |
高(主界面设备/source/sink 全走) |
| RuntimeTarget UI |
★ 左侧资源监控 Dock + native 选项(本 ADR §3.1) |
复用 XiLink |
复用 XiLink |
| 右侧 Dock 节点 |
4 类(source / sink / 输入设备 / 输出设备) |
同 XiLink |
无右侧 Dock(主界面直出) |
| log_module 追踪 |
★ 各 Meter Dock 内追踪(本 ADR §3.6) |
同 XiLink |
xitest meter 组件可选追踪 |
§2.4 ADR-21 / ADR-21-R1 selector 类别校正(本 ADR 修订)
| ADR-21-R1 §3.1 |
本 ADR §3.3 校正 |
| selector 3 类:input / output / log_module(source/sink 归并) |
selector 4 类:source / sink / 输入设备 / 输出设备(log_module 移出独立节点 · 改为 Meter Dock 内部横切追踪能力) |
修订理由:用户 2026-06-18 15:45 verbatim "右侧 dock 不会有专门的 log module" + "可观察的节点只有 source,sink,输入输出设备,logmodule" 中 logmodule 真意是 Meter Dock 内追踪 · 非独立节点。ADR-21-R1 的 3 类归并是早期推断 · 本 ADR 用户校正后回到 4 类(source 与 sink 不归并)+ log_module 横切。
§3 业务行为契约(5 必填段 × 多子决议)
① 输入/输出契约
// IRuntimeTargetService.cs 扩展(新增子类型)
public enum RuntimeKind
{
PcNative, // 现状保留 + 加 NativeSubKind
PcSimulator, // 现状保留
DspHardware, // 现状保留
Remote, // 现状保留
}
public enum NativeSubKind // 🆕 新增
{
Cpu0, Cpu1, Cpu2, Cpu3,
Vdsp0, Vdsp1, Vdsp2, Vdsp3,
}
public interface IRuntimeTargetService
{
RuntimeTargetDescriptor Active { get; }
NativeSubKind? ActiveNativeSubKind { get; } // 🆕 仅当 Kind=PcNative 时有效
void SetActive(string id, NativeSubKind? subKind = null);
IReadOnlyList<RuntimeTargetDescriptor> Available { get; }
}
// 前端 ConnectionDock 选项扩展
type ConnectionType = 'serial' | 'tcp' | 'usb' | 'simulator' | 'native' // 🆕 'native'
// 资源监控 Dock 显示规则
function getTargetPool(kind: RuntimeKind, subKind?: NativeSubKind): string[] {
if (kind === 'PcNative') {
return ['CPU0', 'CPU1', 'CPU2', 'CPU3', 'VDSP0', 'VDSP1', 'VDSP2', 'VDSP3']
}
if (kind === 'DspHardware') return ['DSP-Core-0', 'DSP-Core-1', ...] // 现有
if (kind === 'PcSimulator') return ['Simulator-Slot-A'] // 现有
return []
}
② 收敛/成功判据
| 判据 |
阈值 |
含义 |
| 资源监控 Dock 出现在左侧 |
✅ |
原右侧"指标监控"完整迁移 + 改名 + RuntimeTarget 子面板 |
| 连接 Dock 含 native 选项 |
✅ |
下拉/单选含 native(中文标签:CPU 原生) |
| native 模式 target 池 |
8 项(CPU0-3 + VDSP0-3) |
选 native 后资源监控 dock 显示 8 项 target |
| 切换 native subKind |
< 200ms |
UI 响应 + 后端 SetActive(subKind) 持久化 |
| RuntimeKind=DspHardware target 池 |
现有不变 |
保持 ADR-12 / ADR-21 锚定 |
| AudioEngineService.cs:120 分支 |
兼容 |
DSP/PC 分支不破坏 · NativeSubKind 仅作元信息 |
③ 失败回退路径(5 类)
| 失败 |
触发 |
UI 表现 |
恢复路径 |
| native 选项点击但后端未响应 |
RuntimeTargetRegistry 未注册 NativeSubKind |
toast "native 模式注册中" + 资源监控 dock 显示 loading |
F1-2 后端注册补完 |
| VDSP source 走 DSP 算法栈失败 |
dspalgo.dll 加载失败 |
退化为 CPU 模式(自动切 CPU0)+ toast 警告 |
用户重启或切真 DSP |
| 资源监控 dock 与原指标监控数据丢失 |
迁移过程数据未透传 |
旧指标列表 fallback 显示(不丢功能) |
F1-1 worker 实施时严格保留原数据源 |
| 用户在 native 模式下切到 DspHardware |
跨连接类型切换 |
提示"切换需重启 engine" + 二次确认 |
用户拍板后 AudioEngineService.Restart() |
| 资源监控 Dock 拖动到右侧(用户误操作) |
布局拖拽 |
允许(布局自由) · 但默认载入位置左侧 |
layout state 持久化 ADR-15 复用 |
④ 用户操作流(端到端 6 步)
- 用户进入 XiLink Stage
- 左侧 Dock 看到"连接"+"资源监控"两项(资源监控原属右侧第一)
- 点连接 Dock → 底层 DSP 通信 → 选 native(新增选项)→ engine 切 PcNative
- 切到资源监控 Dock → 看到 target 池下拉:CPU0 / CPU1 / CPU2 / CPU3 / VDSP0 / VDSP1 / VDSP2 / VDSP3(8 项)
- 选 VDSP0 → 后端 SetActive(PcNative, Vdsp0) → source 信号链切走 DSP 算法栈(虚拟 DSP)
- 链路加载 → meter 双链路并存(链路 1 探针 + 链路 2 右侧 Dock + log_module 追踪)正常工作
⑤ 端到端真值 e2e
// frontend_vue3/e2e/scenarios/adr-26-runtime-target/native-vdsp-switch.spec.ts
test('§3.1 ⑤ · native 连接 + VDSP target 切换 + source 路径切到 DSP 算法栈', async ({ page }) => {
await page.goto('/xilink')
// 左侧 dock 含资源监控 + 连接
await expect(page.locator('[data-dock-key="resource-monitor"][data-side="left"]')).toBeVisible()
await expect(page.locator('[data-dock-key="connection"][data-side="left"]')).toBeVisible()
// 选 native
await page.click('[data-dock-key="connection"]')
await page.selectOption('[data-test="connection-type"]', 'native')
// 资源监控 target 池 8 项
await page.click('[data-dock-key="resource-monitor"]')
const options = await page.locator('[data-test="runtime-target-option"]').allTextContents()
expect(options).toEqual(['CPU0', 'CPU1', 'CPU2', 'CPU3', 'VDSP0', 'VDSP1', 'VDSP2', 'VDSP3'])
// 选 VDSP0
await page.click('[data-test="runtime-target-option"]:has-text("VDSP0")')
// 真值断言:注入 1kHz @ -10dBFS · 验证 source 经 DSP 算法栈(链路 1 探针可观察 · 与 CPU0 模式信号链不同)
await injectSignal({ freq: 1000, level: -10 })
const fftFrame = await captureFftFrame()
expect(fftFrame.peakBin.freq).toBeCloseTo(1000, 5)
expect(fftFrame.metadata.signalPath).toBe('vdsp') // 元数据标记
})
§3.2 链路 1 · dspalgo C 实时探针(锚定 ADR-18 + ADR-21 不变)
① 输入/输出契约
// 复用 ADR-18 §3.3 + ADR-21 §3 · 本 ADR 仅锚定 frame schema 单点
// contracts/ 目录(ADR-22 F1 已落地 6 schema 基线 · 本 ADR 复用)
interface FftFrame { bins: number[]; peakFreq: number; peakDb: number; ts: number; sampleRate: number }
interface RmsFrame { rms: number; peak: number; lufs?: number; ts: number; channel: number }
interface PhaseFrame { phaseDeg: number[]; freq: number[]; groupDelay: number[]; ts: number }
interface TransferFrame { magDb: number[]; phaseDeg: number[]; coherence: number[]; freq: number[]; ts: number }
interface ScopeFrame { samples: number[]; trigger?: { level: number; edge: 'rising'|'falling' }; ts: number }
② 收敛/成功判据
| 判据 |
阈值 |
含义 |
| typeId 范围 |
0x100E0001 ~ 0x100E0005 |
rms/fft/scope/phase/transfer 五 module 严格沿用 |
| polling 频率 |
rate-limited(MeterRateLimiter.cs) |
不阻塞实时 audio 线程 |
| frame 单位精度 |
dB ± 0.5 / Hz ± 1 |
与链路 2 数据对账判据 |
| 零拷贝 |
✅ |
dsp_algo 就地计算 · 不出 PCM |
| WS push 延迟 |
≤ 30ms |
RealtimeWsRoutes.cs 推送 |
③ 失败回退路径(5 类)
| 失败 |
触发 |
UI 表现 |
恢复路径 |
| typeId 加载失败 |
dsp_algo.dll 版本不匹配 |
dock loading + 红字 "module not loaded" |
重启 backend |
| polling 超时 |
C 算法死循环 |
dock 显示最后一帧 + 灰化 |
dsp_algo 健康检查重启 module |
| WS 断连 |
网络抖动 |
dock 显示 "WS disconnected" + 自动重连 |
useDockMeterFrame 内置重连 |
| frame schema 字段缺失 |
老 dsp_algo 版本 |
dock 退化(只显示部分字段) |
升级 dsp_algo + frontend 同步 |
| rate limit 触发 |
用户拖多个 module |
自动降帧(60Hz→30Hz)+ toast 提示 |
用户减少同时观察的 module |
④ 用户操作流(端到端 5 步)
- 用户在 XiLink Canvas 拖 fft_module 到链路中间
- Backend 加载 typeId 0x100E0002 + 实例化 fan-out tap(1 in 1 out passthrough)
- dsp_algo 就地算 FFT(零拷贝 · 不阻塞 audio)
- polling → WS push FftFrame → DrawerDockFft.vue
- 前端零数学渲染频谱(ADR-07 三层分工铁律)
⑤ 端到端真值 e2e
(复用 ADR-21 F7 / ADR-22 F15 已规划的 e2e · 本 ADR 不重复)
§3.3 链路 2 · 右侧 Dock 4 类节点(校正 ADR-21-R1)
① 输入/输出契约
// 右侧 Dock 4 类可观察节点(本 ADR §2.4 校正)
type RightDockNodeKind = 'source' | 'sink' | 'input-device' | 'output-device'
// log_module 不在 4 类内(由 Meter Dock 内部追踪 · 见 §3.6)
// PC 模式 source/sink fork PCM
GET /source/{id}/pcm-fork → PCM bytes (短窗 · backend 持有)
GET /sink/{id}/pcm-fork → PCM bytes
// DSP 模式 source/sink 隐藏(UI 灰)
// 输入/输出设备永远可观察
GET /devices/wasapi/{id}/capture → PCM bytes (WASAPI 直采)
// 4 类节点统一 sidecar 分析端点
POST /analyze/fft { file_or_pcm, ... } → FftAnalysisResult
POST /analyze/rms { ... } → RmsAnalysisResult
POST /analyze/phase { ... } → PhaseAnalysisResult
POST /analyze/transfer { ref, measure, ... } → TransferAnalysisResult
POST /analyze/scope { ... } → ScopeAnalysisResult
② 收敛/成功判据(节点矩阵)
| 节点类型 |
PcNative / PcSimulator |
DspHardware |
所属链路 |
| source |
✅ 亮(backend fork PCM) |
❌ 隐藏/灰 |
链路 2 |
| sink |
✅ 亮(backend fork PCM) |
❌ 隐藏/灰 |
链路 2 |
| 输入设备(WASAPI) |
✅ 亮 |
✅ 亮 |
链路 2 |
| 输出设备(WASAPI) |
✅ 亮 |
✅ 亮 |
链路 2 |
| 判据 |
阈值 |
含义 |
| RuntimeKind=DspHardware |
source/sink 灰 + 不可点击 |
UI 自动隐藏 |
| RuntimeKind=PcNative/PcSimulator |
4 类全亮 |
全可点击 |
| pcm-fork 短窗 |
≤ 2 秒 |
避免长 PCM 占内存 |
| sidecar 分析延迟 |
≤ 500ms(2s 窗 FFT) |
用户感知响应 |
| 4 类节点单击右键菜单 |
含 5 种分析(fft/rms/phase/scope/transfer) |
统一交互 |
③ 失败回退路径(5 类)
| 失败 |
触发 |
UI 表现 |
恢复路径 |
| pcm-fork backend 异常 |
SourceSignalService.cs 未启动 |
toast "PCM 抽样失败" + dock 灰 |
重启 backend |
| sidecar HTTP 504 |
numpy 计算卡死 |
dock 显示 loading + 30s 超时 |
sidecar 健康检查重启 |
| DSP 模式用户强行点 source |
UI 校验失效 |
toast "DSP 模式 source 不可观察" |
UI 修复(F2-1) |
| WASAPI 设备拔出 |
硬件断开 |
dock 显示 "device unplugged" |
用户重插 + UI 自动恢复 |
| 4 类节点同时大量请求 |
用户狂点 |
sidecar 队列限流 + toast |
自动队列管理 |
④ 用户操作流(端到端 5 步)
- 用户在 XiLink 选择 PcNative + CPU0(§3.1)
- 右侧 Dock 显示 4 类节点(source / sink / 输入设备 / 输出设备 · 全亮)
- 用户右键 source → 选"看频谱" → backend fork PCM → sidecar /analyze/fft → popup 显示 FFT
- 用户切到 DspHardware → 右侧 Dock source/sink 灰(隐藏)· 输入/输出设备保持亮
- 用户右键 输入设备 → 选"看 RMS" → WASAPI 直采 → sidecar /analyze/rms → popup 显示 RMS
⑤ 端到端真值 e2e
test('§3.3 ⑤ · 4 类节点矩阵 + DSP 模式 source/sink 隐藏', async ({ page }) => {
await page.goto('/xilink')
// PcNative 模式
await switchRuntime(page, 'PcNative', 'CPU0')
await expect(page.locator('[data-right-dock-node="source"]:not(.disabled)')).toBeVisible()
await expect(page.locator('[data-right-dock-node="sink"]:not(.disabled)')).toBeVisible()
await expect(page.locator('[data-right-dock-node="input-device"]:not(.disabled)')).toBeVisible()
await expect(page.locator('[data-right-dock-node="output-device"]:not(.disabled)')).toBeVisible()
// 切 DspHardware
await switchRuntime(page, 'DspHardware')
await expect(page.locator('[data-right-dock-node="source"].disabled')).toBeVisible()
await expect(page.locator('[data-right-dock-node="sink"].disabled')).toBeVisible()
await expect(page.locator('[data-right-dock-node="input-device"]:not(.disabled)')).toBeVisible()
await expect(page.locator('[data-right-dock-node="output-device"]:not(.disabled)')).toBeVisible()
// 真值断言:source 1kHz@-10dBFS · sidecar /analyze/fft · 峰值 1kHz ± 1Hz / -10 dB ± 0.5
await switchRuntime(page, 'PcNative', 'CPU0')
await page.click('[data-right-dock-node="source"][data-test-action="fft"]')
const fft = await page.locator('[data-test="popup-fft-result"]').textContent()
expect(fft).toMatch(/peak.*1000\s*Hz.*-10\.\d\s*dB/)
})
§3.4 XiTest 实时模式 meter 路径(锚定 + 校正 ADR-12 / ADR-22)
① 输入/输出契约
// XiTest 实时模式主路径 = 链路 2(pysidecar)
// xitest 主界面节点(stages/xitest/realtime/*.vue)调用统一走 sidecar HTTP
GET /devices/wasapi/{id}/capture
GET /source/{id}/pcm-fork
GET /sink/{id}/pcm-fork
POST /analyze/{fft|rms|phase|scope|transfer}
// xitest meter 组件(test-aux/meter/*.vue)
// - RMSMeter / SpectrumChart / PhaseChart / WaveformChart / FreqResponseChart
// - 主路径接收 sidecar HTTP 响应 · 渲染层零数学
// 仅当用户在链路中插了 meter module(链路 1)时 · xitest meter 组件可订阅 useMeterWs.ts WS frame
// 大部分场景不订阅 WS · 走 HTTP 周期拉取或一次性分析
② 收敛/成功判据
| 判据 |
阈值 |
含义 |
| 主路径走链路 2 |
≥ 95% |
设备/source/sink 全走 sidecar HTTP |
| 链路 1 例外条件 |
用户主动插 meter module |
useMeterWs.ts 才订阅 WS |
| xitest 主界面响应 |
≤ 500ms(2s 窗) |
与 §3.3 ② 一致 |
| 与 XiLink 数据对账 |
dB ± 1(同节点 同窗) |
链路 2 ↔ 链路 1 一致性 |
| ADR-22 兼容 |
F10/F12 兼容 |
xitest UX 大重构 widget 复用 |
③ 失败回退路径(5 类)
| 失败 |
触发 |
UI 表现 |
恢复路径 |
| sidecar 离线 |
pysidecar 进程崩 |
xitest 主界面 toast "分析服务离线" + 退化 |
backend 自动重启 sidecar(ADR-12) |
| WASAPI 直采权限 |
OS 权限 |
toast "需授权 WASAPI" |
用户授权 |
| xitest 切到链路 1 失败 |
用户插 meter module 但 dsp_algo 故障 |
退化为链路 2(自动 fallback) |
dsp_algo 重启 |
| 大量 device 同时采集 |
多设备勾选 |
队列限流 + toast |
sidecar 队列管理 |
| ADR-22 widget loading |
F1-F6 算法未落地 |
widget loading + 占位 |
ADR-22 实施完成 |
④ 用户操作流(端到端 5 步)
- 用户进 XiTest 实时模式
- 主界面看到设备 + source + sink 主区(全走链路 2)
- 选输入设备 → WASAPI 直采 → sidecar /analyze/fft → SpectrumChart 渲染
- 选 source → backend fork PCM → sidecar /analyze/rms → RMSMeter 渲染
- (可选)用户切 XiLink 在链路插 fft_module → xitest meter 订阅 WS frame(链路 1)
⑤ 端到端真值 e2e
test('§3.4 ⑤ · xitest 实时模式主路径走链路 2', async ({ page }) => {
await page.goto('/xitest/realtime')
// 设备主界面
await page.click('[data-test="xitest-device-input-1"]')
await page.click('[data-test="xitest-show-spectrum"]')
// 真值断言:WASAPI 直采 → sidecar /analyze/fft
const requests = await captureNetworkRequests(page, /\/analyze\/fft/)
expect(requests).toHaveLength(1)
expect(requests[0].url).toContain('pysidecar') // 链路 2
// 注入 1kHz · 验证主界面显示
await injectSignal({ freq: 1000, level: -10 })
await expect(page.locator('[data-test="xitest-spectrum-peak-freq"]')).toHaveText(/1000/)
})
§3.5 log_module 双击 = 落盘类型选择(本 ADR 新决议)
① 输入/输出契约
// log_module 双击 → 落盘配置 Dialog(无分析 UI)
interface LogModuleConfig {
audioLog: {
enabled: boolean
format: 'wav-pcm16' | 'wav-pcm24' | 'wav-float32'
sampleRate: number // 默认继承链路
channels: number // 默认继承链路
fileNameTemplate: string // e.g. "log_${timestamp}_${moduleId}.wav"
}
textLog: {
enabled: boolean
format: 'csv' | 'json'
fields: string[] // e.g. ['ts', 'rms', 'peak', 'event']
fileNameTemplate: string // e.g. "log_${timestamp}_${moduleId}.csv"
}
}
// API
PUT /log-module/{id}/config { config: LogModuleConfig } → 200 OK
GET /log-module/{id}/config → LogModuleConfig
② 收敛/成功判据
| 判据 |
阈值 |
含义 |
| 双击 log_module 不开分析 UI |
✅ |
不弹 popup 频谱/RMS 等 · 仅弹落盘 Dialog |
| Dialog 含 audioLog + textLog 双区 |
✅ |
用户可独立勾选 |
| 默认值合理 |
wav-pcm16 + 链路采样率 + 链路通道 |
用户秒确认 |
| 配置应用延迟 |
< 200ms |
PUT 后 log_module 实时切换 |
| 文件名模板支持 ${timestamp} ${moduleId} |
✅ |
防覆盖 |
③ 失败回退路径(5 类)
| 失败 |
触发 |
UI 表现 |
恢复路径 |
| 落盘磁盘满 |
大文件 + 长时间 |
toast "磁盘空间不足" + 自动停 log |
用户清理 + 重启 log |
| 文件名模板冲突 |
同名覆盖 |
自动加后缀 -1/-2/... |
静默处理 |
| audioLog 采样率与链路不匹配 |
用户改链路后未更新 config |
自动重采样(sidecar) + warning |
用户确认 |
| textLog fields 含未知字段 |
用户输入错 |
Dialog 校验 + 红字提示 |
用户修正 |
| log_module 实例已删除 |
用户在配置中删 module |
Dialog 自动关闭 + toast |
静默处理 |
④ 用户操作流(端到端 5 步)
- 用户在 XiLink Canvas 链路中拖入 log_module
- 用户双击 log_module 节点
- 弹出"落盘配置" Dialog(audioLog + textLog 双区)
- 用户勾选 ☑ Audio Log (wav-pcm16) + ☐ Text Log + 文件名模板 → 确认
- log_module 实时落盘 wav 到
output/log_${ts}_${id}.wav · 无分析 UI 弹出
⑤ 端到端真值 e2e
test('§3.5 ⑤ · 双击 log_module = 落盘配置 Dialog · 不弹分析 UI', async ({ page }) => {
await page.goto('/xilink')
await dragModuleToChain(page, 'log_module')
await page.dblclick('[data-module-type="log_module"]:last-child')
// 弹出落盘 Dialog · 不是分析 popup
await expect(page.locator('[data-test="log-module-config-dialog"]')).toBeVisible()
await expect(page.locator('[data-test="popup-fft"]')).not.toBeVisible()
await expect(page.locator('[data-test="popup-rms"]')).not.toBeVisible()
// Dialog 含 audioLog + textLog
await expect(page.locator('[data-test="config-audio-log-enabled"]')).toBeVisible()
await expect(page.locator('[data-test="config-text-log-enabled"]')).toBeVisible()
// 配置 + 确认
await page.check('[data-test="config-audio-log-enabled"]')
await page.fill('[data-test="config-audio-log-filename"]', 'log_${timestamp}.wav')
await page.click('[data-test="config-confirm"]')
// 验证落盘
await injectSignal({ freq: 1000, level: -10, duration: 2 })
const wavFile = await waitForFile('output/log_*.wav', { timeout: 5000 })
expect(wavFile).toBeTruthy()
})
§3.6 log_module 横切追踪(Meter Dock 内部 · 本 ADR 新决议)
① 输入/输出契约
// 各 Meter Dock(FFT/RMS/Phase/Scope/Transfer)内部加入 log_module 追踪器
interface LogModuleTracker {
listChainLogModules(): LogModuleSummary[] // 从当前链路 grep 所有 log_module 实例
selectLogModule(id: string): void // 选某个
analyzeLogWav(id: string): Promise<AnalysisResult> // 走 sidecar
}
interface LogModuleSummary {
id: string
name: string // e.g. "log_pre_eq"
filePath: string // 最近落盘的 wav
sampleRate: number
channels: number
durationSec: number
}
// API
GET /chain/log-modules → LogModuleSummary[]
// Meter Dock UI
// FFT Dock 内顶部新增下拉:[追踪 log_module ▼ log_pre_eq | log_post_eq | log_sink]
// 选某个 → POST /analyze/fft?file=output/log_pre_eq.wav → FFT Dock 内绘图
② 收敛/成功判据
| Meter Dock 类型 |
追踪能力 |
分析路径 |
| FFT Dock |
列出链路所有 log_module → 选 1 |
sidecar /analyze/fft |
| RMS Dock |
同上 |
sidecar /analyze/rms |
| Phase Dock |
同上(双通道时启用) |
sidecar /analyze/phase |
| Scope Dock |
同上 |
sidecar /analyze/scope |
| Transfer Dock |
同上(需 ref + measure 双 log_module) |
sidecar /analyze/transfer |
| 判据 |
阈值 |
含义 |
| Dock 内追踪下拉响应 |
< 100ms |
列表实时刷新 |
| 切 log_module 重渲染 |
< 500ms |
与 §3.3 ② 一致 |
| Transfer Dock 双选支持 |
✅(ref + measure) |
UI 双下拉 |
| 链路无 log_module 时 |
下拉显示 "无 log_module" + 引导文案 |
用户拖 log_module 进链路 |
③ 失败回退路径(5 类)
| 失败 |
触发 |
UI 表现 |
恢复路径 |
| log_module wav 文件不存在 |
未启动 log 或文件被删 |
下拉旁红字 "无 wav · 启动落盘后再选" |
用户确认 §3.5 配置 |
| sidecar 分析失败 |
与 §3.3 ③ 一致 |
dock loading |
重启 sidecar |
| 链路无 log_module |
下拉空 |
引导文案"在链路中拖入 log_module 后追踪" |
用户操作 §3.5 |
| log_module 落盘格式不兼容 |
wav 编码异常 |
toast "格式不兼容" |
检查 §3.5 config.format |
| Transfer Dock 用户只选 1 个 |
单选 |
提示"需选 ref + measure 双通道" |
用户补选 |
④ 用户操作流(端到端 5 步)
- 用户在链路中拖入 2 个 log_module(log_pre_eq + log_post_eq)+ 配置落盘 wav(§3.5)
- 用户开 FFT Dock(右键链路 / 或菜单)
- FFT Dock 顶部下拉显示 [追踪 log_module ▼ log_pre_eq | log_post_eq]
- 用户选 log_pre_eq → sidecar /analyze/fft → FFT 绘图
- 用户切 log_post_eq → 同节点切换分析 · 实时对比 EQ 前后频谱
⑤ 端到端真值 e2e
test('§3.6 ⑤ · FFT Dock 追踪 log_module + 切换重渲染', async ({ page }) => {
await page.goto('/xilink')
await dragModuleToChain(page, 'log_module', { name: 'log_pre_eq' })
await dragModuleToChain(page, 'log_module', { name: 'log_post_eq' })
await configureLogModule(page, 'log_pre_eq', { audioLog: true })
await configureLogModule(page, 'log_post_eq', { audioLog: true })
await injectSignal({ freq: 1000, level: -10, duration: 2 })
await openMeterDock(page, 'fft')
// 下拉含 2 项
const options = await page.locator('[data-test="fft-dock-log-tracker-option"]').allTextContents()
expect(options).toEqual(['log_pre_eq', 'log_post_eq'])
// 选 log_pre_eq
await page.click('[data-test="fft-dock-log-tracker-option"]:has-text("log_pre_eq")')
await expect(page.locator('[data-test="fft-dock-peak-freq"]')).toHaveText(/1000/)
// 切 log_post_eq
await page.click('[data-test="fft-dock-log-tracker-option"]:has-text("log_post_eq")')
// 验证切换 + 重渲染
await expect(page.locator('[data-test="fft-dock-current-source"]')).toHaveText(/log_post_eq/)
})
§3.7 XiTune meter 复用(锚定 · 不变更代码)
XiTune meter 复用 XiLink 的双链路 + Meter Dock + 节点矩阵 + log_module 横切。本 ADR 不新增 fork · 仅在 §4 §"实施清单"中标注 XiTune 验证用例(F4-1 e2e)。
XiTune 特有:
- auto_tune 子模块(target_loader / deviation / simulate / averaging / smoothing)走 sidecar HTTP /auto_tune/*
- FreqResponseChart(频响目标对比)复用 §3.6 log_module 追踪能力(ref=目标曲线 wav · measure=实测 log_module)
§4 实施清单(Implementation Forks)
§4.1 Fork 表
| F# |
UID |
部门 |
Worker |
工作量 |
描述 |
blocked-by |
| F1-1 |
P5.A26.F1-runtime-target-backend-extension |
后端 |
ClaudeB |
1.5d |
IRuntimeTargetService 加 NativeSubKind enum + RuntimeTargetRegistry 注册 CPU0-3/VDSP0-3 + AudioEngineService.cs:120 分支兼容 + 持久化 NativeSubKind 到 workspace · 8 vitest case |
无(ready) |
| F1-2 |
P3.A26.F1-runtime-target-frontend-ui |
前端 P1-xilink |
ClaudeA |
2.0d |
资源监控 Dock 左移 + 改名 + RuntimeTarget 子面板 + 连接 Dock 加 native 选项 + native 模式 8 项 target 池下拉 · 与 ADR-15 layout 持久化兼容 · 6 vitest + 2 playwright |
F1-1 |
| F2-1 |
P3.A26.F2-right-dock-4-categories-correction |
前端 P1-xilink |
ClaudeA |
1.0d |
ADR-21-R1 的 3 类 selector 校正为 4 类(source/sink/输入设备/输出设备 · 不归并 source/sink)+ DSP 模式 source/sink 自动隐藏/灰 + RuntimeKind 监听 · 5 vitest + 2 playwright |
无(可与 F1 并行 · 文件正交) |
| F3-1 |
P3.A26.F3-log-module-double-click-config-dialog |
前端 P1-xilink + P5 后端 |
ClaudeA + ClaudeB |
1.5d |
log_module 双击改弹落盘配置 Dialog(audioLog + textLog 双区)+ PUT /log-module/{id}/config 后端端点 + 文件名模板渲染 · 不弹分析 UI · 6 vitest + 2 playwright |
无(ready) |
| F3-2 |
P3.A26.F3-meter-dock-log-module-tracker |
前端 P1-xilink |
ClaudeA |
1.5d |
5 Meter Dock 内顶部新增 log_module 追踪下拉 + 切换重渲染 + Transfer Dock 双选 + GET /chain/log-modules 后端端点 · 8 vitest + 2 playwright |
F3-1 |
| F4-1 |
P_e2e.A26.F4-truth-e2e-meter-dual-link 🏆 |
测试编排 |
ClaudeC |
1.5d |
playwright integration spec 6 test(§3.1 native+VDSP / §3.3 4 类节点矩阵 + DSP 隐藏 / §3.4 xitest 链路 2 主路径 / §3.5 log_module Dialog / §3.6 Meter Dock 追踪 / §3.7 xitune 复用)+ DSP 注入 1kHz @ -10dBFS 真值断言 · 解锁 ADR-26 fulfilled 🏆 |
F1-1+F1-2+F2-1+F3-1+F3-2 全 zombie |
| 合计 |
|
|
|
9.0d |
5 fork + 1 e2e · 跨栈(后端 + 前端 + e2e)· 关键路径 F1-1 → F1-2 → F4-1 = 5.0d · 与 F2-1+F3-1+F3-2 文件正交可并行 |
|
§4.2 关键路径与并行结构
F1-1 (1.5d 后端)
│
├─→ F1-2 (2.0d 前端 RuntimeTarget UI) ╲
│ ╲
F2-1 (1.0d 前端 4 类 selector · 独立) ────╲
╲
F3-1 (1.5d log_module Dialog · 独立) ────── ───→ F4-1 (1.5d e2e 🏆)
│ ╱
└─→ F3-2 (1.5d Meter Dock tracker) ───╱
关键路径:F1-1 → F1-2 → F4-1 = 5.0d
ClaudeA 队列(全前端):F1-2 + F2-1 + F3-1 + F3-2 串行 = 6.0d(若不并行)· 文件正交可并行 max=2.0d
ClaudeB 队列:F1-1 后端(1.5d)+ F3-1 后端协作(0.5d) = 2.0d
ClaudeC:F4-1 e2e(1.5d)
§4.3 文件正交矩阵
| Fork |
主要文件 |
与其他 fork 冲突? |
| F1-1 |
backend_csharp/Services/Runtime/IRuntimeTargetService.cs + RuntimeTargetRegistry.cs + AudioEngineService.cs |
与 F3-1 后端正交(F3-1 改 LogModuleService) |
| F1-2 |
frontend_vue3/src/stages/xilink/drawers/ResourceMonitorDock.vue(新建)+ ConnectionDock.vue(改) |
与 F2-1 正交(F2-1 改 RightDock*) |
| F2-1 |
xilink/drawers/RightDock*.vue + composables/useChainNodeMetadata.ts(改 4 类) |
与 F1-2 正交 |
| F3-1 |
xilink/nodes/LogModuleNode.vue(改双击)+ components/LogModuleConfigDialog.vue(新建)+ backend Routes/LogModuleRoutes.cs(改) |
与 F1-1/F1-2/F2-1 正交 |
| F3-2 |
xilink/drawers/Drawer Dock{Fft,Rms,Phase,Scope,Transfer}.vue(改加 tracker 下拉)+ composables/useLogModuleTracker.ts(新建) |
与 F3-1 串行(GET /chain/log-modules 后端协作) |
§5 风险与缓解
| 风险 |
缓解 |
| F1-1 NativeSubKind 注册触动 AudioEngineService.cs:120 分支 · 风险破坏 PC/DSP 现状 |
F1-1 prompt Step 0 强制 read AudioEngineService.cs:80-150 + IRuntimeTargetService.cs 全文 + grep "RuntimeKind" 全 backend · 不破坏现有分支 · NativeSubKind 仅作元信息 |
| F1-2 资源监控 Dock 迁移可能丢原指标列表数据源 |
F1-2 prompt Step 0 强制 read 原右侧"指标监控"drawer 全文 + 数据源 service · 严格保留(整体 move 不重写) |
| F2-1 校正 ADR-21-R1 3 类 → 4 类 · 可能与已落地 c780836 冲突 |
F2-1 prompt 含 git show c780836 完整 read · 评估 supersede 范围(归并逻辑 revert · 4 类隔离重做)· 与 ADR-21-R1 R1.1 hotfix 教训承接 |
| F3-1 log_module 双击行为变更 · 可能用户不适应(原行为推测是分析 popup) |
F3-1 prompt Step 0 grep 现有 LogModuleNode.vue 双击行为 · 列出所有调用点 · 用户操作流写明"原分析 UI 移到 Meter Dock 追踪"(§3.6 引导)+ Dialog 含"了解更多"链接 |
| F3-2 5 Meter Dock 同步加 tracker · 改动面广 |
F3-2 prompt 拆 5 子步骤(每个 Dock 独立)+ vitest 每个 Dock 独立 case · 渐进 commit |
| F4-1 e2e 6 test 注入信号 + 跨 stage(xilink/xitest/xitune)· fixture 复杂 |
F4-1 prompt 复用 ADR-21 / ADR-22 已有 fixture 体系 · 仅新增 ADR-26 特定 fixture 1-2 个(含 log_module 落盘 wav 预录) |
| ClaudeA 排队膨胀(F1-2 + F2-1 + F3-1 + F3-2 共 6.0d 串行)· 阻塞其他 ADR |
F2-1 + F3-1 与 F1-2 文件正交 · 可三并行起手(等 F1-1 zombie 后)· 实际跨度 max=2.0d |
| dsp_algo 子仓污染清理用户已自处理 · 但本 ADR 实施时如仍有遗留 commit 错路径 |
F1-1 / F3-1 prompt 含 v4 教训段:"DSP 类任务真值仓 = dsp_algo/ 独立 git 仓库" · 严禁在 04_development 父仓 commit dsp_algo/* 路径 |
| ADR-22 widget 算法 F3-F6 跨栈 dsp_algo 任务可能与本 ADR §3.2 锚定冲突 |
本 ADR §3.2 仅锚定路径 · 不变更 dsp_algo 代码 · ADR-22 实施进度独立 · 兼容 |
§6 决议者签名
- proposed by:Cline-AIOS(2026-06-18 12:30)
- revision triggers:user(2026-06-18 12:30 verbatim 双链路矩阵 + RuntimeTarget 改造 + 15:45 verbatim log_module 修正 + xitest/xitune 延展 + 16:46 启动 ADR)
- review by:user(框图 HTML v0.2 已确认)
- accepted by:user(2026-06-18 16:46 启动 = 直接 accepted · v3.0 无 proposed 二阶段)
§7 状态流转
| 时间 |
事件 |
备注 |
| 2026-06-18 12:30 |
用户首轮 verbatim |
双链路矩阵 + RuntimeTarget 改造 4 项 |
| 2026-06-18 15:45 |
用户二轮 verbatim |
log_module 修正 + xitest/xitune 延展 |
| 2026-06-18 15:50 |
HTML 框图 v0.2 落盘 |
5 图 + 4 sequence + 4 表 · 用户审阅 |
| 2026-06-18 16:46 |
用户拍板"启动 ADR" |
v3.0 直接 accepted |
| 2026-06-18 16:50 |
ADR-26 v0.1 accepted |
6 fork ready · F1-1 + F2-1 + F3-1 三并行可起手 · F1-2/F3-2/F4-1 串行 |
| (待) |
F1-1 + F2-1 + F3-1 三并行 dispatched |
ClaudeB 后端 + ClaudeA 前端 ×2 |
| (待) |
F1-2 + F3-2 串行 dispatched |
F1-1 / F3-1 zombie 后接力 |
| (待) |
F4-1 e2e dispatched |
5 fork zombie 后启动 |
| (待) |
ADR-26 fulfilled 🏆 |
v5.0+ 第 N 个 ADR 闭环 · 三 Stage meter 路径真闭环 |
§8 关联文档
- ADR-AIOS-07(三层分工 · 本 ADR §3.2 锚定"前端零数学")
- ADR-AIOS-12(xitest realtime 架构 · 本 ADR §3.4 锚定主路径)
- ADR-AIOS-13(xitest dual-mode · 本 ADR 兼容)
- ADR-AIOS-18(plugin 协议 · typeId 0x100E0001~5 沿用)
- ADR-AIOS-21(本 ADR §3.5 / §3.6 锚定)
- ADR-AIOS-21-R1(本 ADR §2.4 校正 3 类 → 4 类)
- ADR-AIOS-22(本 ADR §3.4 兼容)
- 框图基线:Meter-Architecture-Framework.html v0.2
- backend 真值:
backend_csharp/Services/Runtime/IRuntimeTargetService.cs L21-27 + RuntimeTargetRegistry.cs + AudioEngineService.cs:120 + Services/Source/SourceSignalService.cs L7-19 + Services/Meter/{AudioDeviceService, MeterRateLimiter, MeterTapService, MeterToolRouter, PysidecarMeterBridge}.cs + Native/AnalysisModuleNative.cs + Routes/{MeterDevApiRoutes, RealtimeWsRoutes, LinkAuditRoutes, LegacyTestRoutes}.cs
- dsp_algo 真值:
dll/dspalgo_dll.h:442 + modules/analysis/{rms,fft,scope,phase,transfer}_module.{c,h}
- pysidecar 真值:
pysidecar/main.py(702 行 FastAPI · 30+ endpoints)+ analyzer/*.py(全 numpy/scipy)
- frontend_vue3 真值:
composables/{useDockMeterFrame, useWidgetEndpoint, useChainNodeMetadata, useDeviceStatus}.ts + stages/xilink/drawers/Drawer Dock{Fft,Rms,Phase,Scope,Transfer}.vue + components/popups/*ModulePopup.vue + test-aux/meter/*.vue
§9 教训沉淀(本 ADR 起草过程)
§9.1 框图先行 + ADR 跟进(v3.0 标杆模式)
本 ADR 是 v3.0 模式下首个采用"框图先行 → 用户多轮校正 → ADR 起草"的标杆 ADR。流程:
1. 用户提出业务问题(双链路 + RuntimeTarget + 三 Stage 统一)
2. Cline-AIOS 不直接起 ADR · 先出 Mermaid 框图(避免空转 · 用户原话)
3. 用户两轮 verbatim 校正(12:30 + 15:45)→ 框图 v0.1 → v0.2
4. 框图 HTML 落盘(独立可执行 · 派 agent 转 HTML 文档基础)
5. 用户拍板"启动 ADR" → 起 ADR-26(本文件)+ 业务契约 5 必填段引用框图基线
沉淀:用户拍板架构方向时 · 优先框图(Mermaid · 多视角)而非 ADR 文字 · 校正成本低 · 收敛快(本 ADR 用户校正 2 轮即收敛 · 远低于 ADR-23→25→25-R1 的 4 小时 2 次 supersede)。
§9.2 ADR-23→25→25-R1 三连教训严格执行
本 ADR §1.3 §"现有组件清单"严格列出 8 项用户原话名词的 grep 真值路径 + 处置(完善 vs 新建)· 不凭推测起草。派发前每个 fork prompt 的 [参考文档] 段必须含 grep 真值路径 + 行号(F1-2 / F2-1 / F3-1 / F3-2 prompt 起草时执行)。
§9.3 校正 ADR-21-R1 3 类 → 4 类(supersede 而非废弃)
本 ADR §2.4 校正 ADR-21-R1 的 selector 3 类(input/output/log_module)为 4 类(source/sink/输入设备/输出设备 · log_module 移出独立节点)。处置:
- ADR-21-R1 主体保留(双击悬浮窗 + cleanup composable 等 · 不变)
- 仅 §3.1 selector 类别 supersede(由本 ADR §3.3 替代)
- F2-1 fork 实施时 git show ADR-21-R1 F1 c780836 · 评估归并逻辑 revert 范围
沉淀:ADR 之间的局部 supersede 必须逐段标注 · 不能整体废弃(避免丢失部分有效资产 · 类比 ADR-25-R1 §2.2/§2.3 资产保留矩阵)。
下一步:accepted 2026-06-18 16:50 → Cline-AIOS 同步 DASHBOARD v5.2.0→v5.2.1(§📋 加 6 fork ready 行 + §📅 加 1 行 + version 升)→ 用户 start P5.A26.F1-runtime-target-backend-extension(关键路径起点)+ 可选三并行 start P3.A26.F2-right-dock-4-categories-correction + start P3.A26.F3-log-module-double-click-config-dialog