跳转至
ACCEPTED

ADR-AIOS-18 · XiLink-Analyze Stage(QXDM-style 链路分析工具)

状态:accepted v0.1 · 2026-06-04 16:14 · 用户拍板路径 A 4 任务并发起手 · 业务契约 5 段 × 4 块全填齐 触发:ADR-17 v0.2 accepted(2026-06-04 15:18)用户拍板选项 B 拆双 ADR · 本 ADR 承载 XiLink-Analyze stage 上层 依赖:ADR-17 §3.3 log module schema + §3.4 RMS meter / scope module schema 底座(必须先 zombie · F5 是关键路径前置)


1. 上下文(Context)

1.1 用户原话存档(verbatim · 防漂移 · 来自 ADR-17 v0.2 §1.1)

1.1.1 第二轮原话(2026-06-04 15:02 · stage 拆分提议来源)

新增问题:按照这个逻辑拆分下来,加载xilink这个显示实时显示不同节点的数据和log已经成为了高通的QXDM的工具了,那我们是否可以拆分一下功能,在realtime前加一个button,主要就是分析xilink的音频流,realtime中主要还是针对输入输出设备的实时曲线显示,和提供APx500专业测量的基本信号生成对比链路;

1.1.2 第三轮拍板(2026-06-04 15:18 · 选项 B 三连)

  • (b) 选项 B:ADR-17 收窄 Realtime stage · 新起 ADR-18 承载 XiLink-Analyze stage
  • (是) RMS meter / scope module schema 固定在 ADR-17 §3.4 底座 · ADR-18 上层只编排
  • (是) F7 e2e 收窄 Realtime stage · xilink-analyze 由 ADR-18 自带 e2e

1.1.3 第四轮指示(2026-06-04 15:51 · 起草节奏)

下一步我希望你借着当前的上下文直接开始ADR-AIOS-18要做的任务,不然后续还要重新定义ADR18

→ Cline-AIOS 一次性起草 v0.1 完整版(避免 ADR-17 v0.1→v0.2 二次返工)。

维度 Realtime Stage(ADR-17) XiLink-Analyze Stage(本 ADR-18)
对标产品 APX500 专业测量 高通 QXDM 链路诊断工具
核心场景 设备测量(物理 mic + 物理 speaker)+ Loopback 信号生成对比 加载 .xilink 工程 → 链路图可视化 → 节点拖入 log/meter/scope → 滚动 polling 显示
IO 配置 APX500 风格 Connector + Device 双下拉 不需要(默认走 loopback / xilink workspace)
Source 生成 Loopback generator(sine/pink/sweep/wav) 不需要(信号来自 .xilink 工程链路本身)
数据呈现 7 widget(RMS/FFT/Phase/Transfer/Waveform/Electrical/Recorder · ADR-12 §3 7 widget 老路径)+ Widget endpoint 4 类 多个独立弹窗 module 自由布局(RMS meter / scope · 类 QXDM 多窗口)+ 滚动 polling 时间轴(类 QXDM 文字 log)
多工程支持 单 workspace 单 session 同一 workspace 多 .xilink 切换 + 节点对比测量 + 快照保存
e2e 验收 7 widget × 2 mode(hardware/loopback)= 14 cell 4 块业务契约 ⑤ 段 e2e(.xilink 加载 / 节点拖入 / 滚动 polling / 多工程切换)

1.3 复用 ADR-17 底座清单(不重复造 schema)

复用项 来源 本 ADR 行为
LogModuleConfig(name + position + captureMode + pcmDump + realtimeStream + textLog) ADR-17 §3.3 ① 仅"拖入 chain 编辑器 + 命名 + 配置 captureMode + 保存到 workspace" UI 编排
DSP 落盘协议(<workspace>/logs/<logmodule-name>-<ts>.{pcm, log.txt}) ADR-17 §3.3 ① 仅"读盘 + 显示文件列表 + 触发回放"UI
WS frame log_module_v1(pcm chunk + textLog line) ADR-17 §3.3 ① + P_contracts protocol-v1 扩展 仅"订阅 + 解码 + 滚动 polling 时间轴渲染"UI
RMSMeterModuleConfig + ScopeModuleConfig 弹窗 component ADR-17 §3.4 ① 仅"chain 编辑器拖入触发弹出 + 多弹窗布局保存到 workspace"UI
WidgetEndpoint 4 大类 enum ADR-17 §3.4 ① 不直接用(本 stage 不显 7 widget)· 但 RMS/scope module 内部 sourceEndpoint 走该 enum

1.4 历史教训沉淀(防与 ADR-08-R1 / ADR-16 重叠)

  • ADR-AIOS-08(XiLink Stage UX · accepted 2026-05-29 · 5 议题决议)+ ADR-08-R1(supersedes by ADR-16)主要做"工程导入 + chain 编辑 + Module Library + Inspector + 右侧 dock + Storage" → 本 ADR 不重复造 chain 编辑器(从 ADR-08 chain 编辑器借用)· 仅扩"分析模式 stage 切换 + log/meter/scope 拖入"
  • ADR-AIOS-16(主图子图统一架构 · proposed)做"SubgraphRuntime 前端扁平化展开 + 7 对 wrapper CRUD 删除" → 本 ADR 不动 SubgraphRuntime· 仅 read-only 引用(链路图渲染时调用)
  • ADR-AIOS-15(workspace persistence)→ 本 ADR log module / RMS / scope 配置 + 多窗口布局走 workspace 持久化(满足 §3.x ③ chain 切换状态保留判据)

2.1 核心架构图(v0.1 · ASCII)

┌────────────────── XiStudio Top Stage Switcher(继承 ADR-17 §2.1)──────────────────┐
│                                                                                     │
│    [▶ Realtime Stage]  |  [🔬 XiLink-Analyze Stage] ★ 本 ADR-18 范围                 │
│      (ADR-17)                (本 ADR-18)                                            │
│                                                                                     │
└─────────────────────────────────────────────────────────────────────────────────────┘
                                       ▼ XiLink-Analyze Stage 内部
┌──────────────────── XiLink-Analyze Stage(本 ADR-18 范围)─────────────────────────────┐
│                                                                                       │
│  ┌─ Top Bar(工程切换 + 录制控制)─────────────────────────────────────────────────┐  │
│  │ Workspace: <name>  |  Active .xilink: [▼ ProjectA.xilink / ProjectB.xilink ...]  │  │
│  │ [▶ Start Polling] [⏸ Pause] [⏹ Stop] [📸 Snapshot] [🗂 Multi-View]               │  │
│  └────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                       │
│  ┌─ Center · Chain Diagram(只读 · 复用 ADR-08 chain 编辑器渲染 + ADR-16 SubgraphRuntime 扁平化)──┐  │
│  │   ┌──────┐    ┌──────┐    ┌──[log mic-tap-1]──┐    ┌──────┐    ┌──[log sink-tap]──┐         │  │
│  │   │source│ →  │  EQ  │ →  │  log module       │ →  │mixer │ →  │  log module       │ → sink │  │
│  │   └──────┘    └──────┘    └───────────────────┘    └──────┘    └───────────────────┘        │  │
│  │                ↑(右键)拖入 [+ log module]/[+ RMS meter]/[+ scope] 三按钮                       │  │
│  └────────────────────────────────────────────────────────────────────────────────────┘          │
│                                       │                                                          │
│         ┌─────────────────────────────┼─────────────────────────────┐                            │
│         ▼                             ▼                             ▼                            │
│  ┌─ RMS Meter Popup ──┐  ┌─ Scope Popup ──────┐  ┌─ Log Module Polling Panel(类 QXDM)─┐         │
│  │ name: rms-after-eq │  │ name: scope-1      │  │ ┌─ 时间轴 ────────────────────────┐  │         │
│  │ src: log/mic-tap-1 │  │ src: log/sink-tap  │  │ │ <pcm waveform 条 · 滚动)      │  │         │
│  │ ▮▮▮▮▮▮▮▮▯▯ -18dB  │  │ [波形 canvas]      │  │ └────────────────────────────────┘  │         │
│  │ peak: -12dB        │  │ latency: 35ms      │  │ ┌─ 文字 log(滚动)─────────────┐  │         │
│  │ refresh: 30fps     │  │                    │  │ │ 12:00:01.234 INFO frame=1024  │  │         │
│  └────────────────────┘  └────────────────────┘  │ │ 12:00:01.275 DEBUG rms=-18.2  │  │         │
│                                                  │ │ 12:00:01.315 INFO frame=1025  │  │         │
│                                                  │ │ ...                          │  │         │
│                                                  │ └────────────────────────────────┘  │         │
│                                                  └────────────────────────────────────┘          │
│                                                                                                  │
│  ┌─ Multi-View(可选 · 多 .xilink 同屏对比)──────────────────────────────────────┐              │
│  │ [ProjectA.xilink chain ↑] [ProjectB.xilink chain ↑] (横向并排 · 各自独立 polling)│              │
│  │ Snapshot 对比:[ProjA snapshot 14:00] vs [ProjB snapshot 14:30] (RMS/scope 重叠)│              │
│  └──────────────────────────────────────────────────────────────────────────────┘              │
│                                                                                                  │
│  ┌─ Bottom · Log file browser(已落盘文件)──────────────────────────────────────┐              │
│  │ <workspace>/logs/                                                                │              │
│  │   - mic-tap-1-20260604-150000.pcm  (10s · 4.6MB) · [回放] [删除] [导出 .wav]    │              │
│  │   - mic-tap-1-20260604-150010.pcm  (...)                                         │              │
│  │   - sink-tap-20260604-150000.log.txt (12KB) · [打开]                             │              │
│  └────────────────────────────────────────────────────────────────────────────────┘              │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
# 需求块 范围 依赖 估时
.xilink 工程加载与链路图渲染 workspace 内 .xilink 列表 + 切换 + chain 状态 read-only API + 链路图渲染(复用 ADR-08 chain 编辑器组件 + ADR-16 SubgraphRuntime 扁平化展开) P5 backend /api/workspace/xilinks API · ADR-08 ChainCanvas 复用 read-only 模式 · ADR-16 SubgraphRuntime(若 accept) 2-3d
Log module / RMS / scope 拖入编排 chain 节点右键菜单 + [+ log module] / [+ RMS meter] / [+ scope] 三按钮 · 配置面板(name + position + sourceEndpoint + captureMode 等)· 配置走 ADR-15 workspace 持久化 F1(.xilink 加载)+ ADR-17 §3.3 LogModuleConfig schema + §3.4 RMSMeterModuleConfig + ScopeModuleConfig + ADR-15 workspace 持久化 1.5-2d
滚动 polling 显示(类 QXDM) Log Module Polling Panel UI · 多 log module 并发订阅 WS log_module_v1 · 时间轴 pcm 波形条 + 文字 log 滚动区 · 暂停/恢复/快照 + 自动滚动 toggle F2(节点拖入) + ADR-17 §3.3 WS frame log_module_v1 + ADR-15 audit log jsonl 1.5-2d
快照对比 + 多工程切换 Multi-View 横向并排显两个 .xilink chain · 各自独立 polling · Snapshot 保存(RMS/scope 当前帧 + 时间戳)· 双 snapshot 重叠对比(同一 endpoint) F1+F2+F3 · ADR-15 workspace snapshot/ 子目录持久化 · localStorage 多窗口布局 1-1.5d
合计 6-9.5d ≈ 1-1.5 周(4 fork 跨栈)

v0.1 范围声明: - ✅ 本 ADR-18 做:XiLink-Analyze stage UI(顶部 button)+ §3.1-§3.4 4 块业务契约 + chain 节点拖入编排 + 滚动 polling 显示 + 多工程切换 - ❌ 本 ADR-18 不做:log module / RMS / scope module schema 定义(全部在 ADR-17 §3.3+§3.4 · F5 zombie 才有)· chain 编辑器写权(本 stage 是 read-only 视图 · 编辑走 XiLink Stage)· IO 设备配置(那是 Realtime stage)· source generator(那是 loopback)


3. 业务行为契约(.clinerules v1.8 §"业务行为契约必填段"· 5 必填段 × 4 块全部填齐 · 对齐 ADR-12 §3 + ADR-17 §3 标杆)

✅ v0.1 完成版 · 4 块共 200+ 行 · 满足 .clinerules v1.8 派发自查清单 7 项 · fork 派发无硬阻塞

① 输入/输出契约

// 前端 TS 接口(P0 + P1)
interface XiLinkAnalyzeStageState {
  workspaceId: string                                  // 当前 workspace
  availableXilinks: XiLinkSummary[]                    // 当前 workspace 内所有 .xilink
  activeXilinkId: string | null                        // 当前激活分析的 .xilink
  multiViewXilinkIds: string[]                         // Multi-View 模式 · ≥ 2 个 .xilink 并排
  chainState: ChainStateReadOnly | null                // 激活 .xilink 的 chain 状态(read-only)
  pollingState: 'idle' | 'starting' | 'polling' | 'paused' | 'error'
}

interface XiLinkSummary {
  id: string
  name: string                                         // 显示名(对齐 ADR-15 F7 name 优先策略)
  filePath: string                                     // <workspace>/<projectId>.xilink
  lastModified: number                                 // ms epoch
  hasLogModules: boolean                               // chain 内是否已含 log module(快速过滤)
  logModuleNames: string[]                             // chain 内 log module 列表(用于 widget endpoint 选择)
}

interface ChainStateReadOnly {
  nodes: ChainNodeReadOnly[]                           // 复用 ADR-08 ChainCanvas read-only 模式渲染
  edges: ChainEdgeReadOnly[]
  // 注:本 stage 不暴露 mutateLink · 不允许编辑 chain · 仅允许追加 log/RMS/scope module(走 §3.2 编排接口)
}

// 后端 REST(P5)
GET  /api/workspace/xilinks                           // → XiLinkSummary[]
GET  /api/workspace/xilinks/:id/chain                 // → ChainStateReadOnly(read-only · 不锁)
POST /api/workspace/xilinks/:id/activate-for-analyze  // 标记为分析激活态(workspace 持久化 lastAnalyzedXilinkId)

② 收敛/成功判据

判据 阈值 含义
.xilink 列表加载 ≤ 300ms 完成 GET /api/workspace/xilinks 列表响应
chain 状态加载 ≤ 500ms 完成 GET /chain + 链路图渲染首帧 切换响应
链路图节点显 hasLogModule 标记 log module 节点显特殊图标(📡) 用户可视化
多 .xilink 切换 200ms 内 chain 渲染切换 UX 流畅性
Multi-View 模式 ≥ 2 工程并排 同时显 ≥ 2 chain · 总渲染 < 1s 多窗口性能
工程列表自动刷新 检测 .xilink 文件 mtime 变化 ≤ 5s 内列表刷新 实时反映 workspace 变化

③ 失败回退路径(5 类)

失败 触发 UI 表现 恢复路径
.xilink 文件损坏 JSON parse 失败 列表中显红色 ⚠ + tooltip "工程文件损坏" + 不允许激活 用户在 XiLink Stage 修复或删除
chain 状态读取失败(后端) 500 / 404 toast "无法加载 chain · 请检查后端" + 链路图区显空白 + retry 按钮 用户点 retry · 自动 5s 间隔
Multi-View 内存超限 同时打开 ≥ 4 .xilink + 8 弹窗 module warn "已开启工程过多 · 关闭部分" + 拒新开 用户关部分工程或弹窗
workspace 切换中分析状态丢失 切 workspace 时 lastAnalyzedXilinkId 不持久 warn "分析状态已重置" · 用户需重选 .xilink 走 ADR-15 workspace 持久化(满足后即不丢)
链路图渲染失败(SubgraphRuntime 异常) ADR-16 SubgraphRuntime 报错 链路图显 "渲染失败 · 显示扁平节点列表" + fallback 列表视图 fallback 用纯节点列表(不显 edges)

④ 用户操作流(5 步)

Step 操作 UI 反馈
1 顶部点 [🔬 XiLink-Analyze Stage] button 切到 XiLink-Analyze stage · Top Bar + 链路图区 + Bottom log browser 显示
2 Top Bar Active .xilink 下拉选 ProjectA.xilink chain 状态 ≤ 500ms 加载 · 链路图渲染节点 + edges
3 链路图显 chain 拓扑(source → EQ → mixer → sink)+ 已有 log module 显 📡 图标 视觉确认 chain 结构
4 (可选)点 Top Bar 🗂 Multi-View → 添加 ProjectB.xilink Multi-View 激活 · 横向并排显两 chain · 各自独立 polling 控件
5 准备进入下一步(块 ② 节点拖入编排) chain 节点右键菜单可用 · [+ log module] 等三按钮就绪

⑤ 端到端真值 e2e(F4 必跑通)

// playwright e2e
test('XiLink-Analyze · .xilink 加载 + chain 渲染 + 切换', async ({ page }) => {
  await page.click('[data-test=stage-switcher-xilink-analyze]')
  await page.waitForSelector('[data-test=xilink-stage-active]')

  // .xilink 列表加载
  const xilinks = await page.locator('[data-test=xilink-list-item]').all()
  expect(xilinks.length).toBeGreaterThanOrEqual(2)  // 至少 2 个 .xilink 用于切换测试

  // 选第一个
  await xilinks[0].click()
  await page.waitForSelector('[data-test=chain-canvas-rendered]', { timeout: 1000 })
  const nodeCount = await page.locator('[data-test=chain-node]').count()
  expect(nodeCount).toBeGreaterThan(0)

  // 切到第二个 · 验 200ms 内切换
  const start = Date.now()
  await xilinks[1].click()
  await page.waitForSelector('[data-test=chain-canvas-rendered]')
  expect(Date.now() - start).toBeLessThan(500)
})

test('XiLink-Analyze · Multi-View 双工程并排', async ({ page }) => {
  await navigateToXilinkAnalyze(page)
  await selectXilink(page, 'ProjectA.xilink')
  await page.click('[data-test=multi-view-toggle]')
  await selectXilink(page, 'ProjectB.xilink', { multiView: true })

  const chainCanvases = await page.locator('[data-test=chain-canvas]').count()
  expect(chainCanvases).toBe(2)
})

[H1 hotfix 修订补丁 · 2026-06-06] 入口架构纠错

原契约把 xilink-analyze 设计为顶层独立 stage(StageBar [🔬] button + /xilink-analyze 独立路由)。 用户 2026-06-06 16:34 反馈:xilink-analyze 不是一个单独的 stage,应该是 xitest 下的集成测试需求。 16:50 进一步澄清:挪到 xitest 下的第二个集成入口,IntegrationTestPanel.vue 老版本搁置不动。

修订后入口契约: - 顶层 StageBar [🔬 XiLink-Analyze Stage] button → 删除 - 独立路由 /xilink-analyze → 删除 - stageStore.StageKey 包含 'xilink-analyze' → 删除 - 新入口:xitest stage 的第 6 个 MODE_CHIP(key: 'xilink-analyze', icon: 🔬, label: XiLink) - 挂载位置stages/xitest/xilink-analyze/index.vue(原 stages/xilink-analyze/ 整体迁入) - 条件渲染shellSlots.activeMode === 'xilink-analyze' 时挂载 XilinkAnalyzeSubPage,否则显示 TestRunnerPanel

F2/F3 已实装的全部功能代码完整保留,仅路径和挂载点重构。

3.2 需求块 ② · Log module / RMS / scope 拖入编排

① 输入/输出契约

// chain 节点右键菜单 action(P0 + P1)
type ChainNodeAnalyzeAction =
  | { type: 'add-log-module'; afterNodeId: string; config: LogModuleConfig }       // 复用 ADR-17 §3.3
  | { type: 'add-rms-meter-module'; afterNodeId: string; config: RMSMeterModuleConfig }  // 复用 ADR-17 §3.4
  | { type: 'add-scope-module'; afterNodeId: string; config: ScopeModuleConfig }   // 复用 ADR-17 §3.4
  | { type: 'remove-analyze-module'; nodeId: string }                              // 仅删 analyze 加的 module · 不动业务 chain

// 配置面板组件(P0)
interface AnalyzeModuleConfigPanelProps {
  moduleType: 'log-module' | 'rms-meter-module' | 'scope-module'
  afterNodeId: string                                  // 锚定位置
  initialConfig?: LogModuleConfig | RMSMeterModuleConfig | ScopeModuleConfig
  existingNames: string[]                              // chain 内已有 log/RMS/scope name(防重名)
  onSave: (config: any) => void
  onCancel: () => void
}

// 后端 API(P5 · 复用 ADR-17 F5 LogModuleService 接口)
POST /api/xilink/:id/add-analyze-module      // body: ChainNodeAnalyzeAction
DELETE /api/xilink/:id/analyze-module/:nodeId
GET  /api/xilink/:id/analyze-modules         // → 当前 chain 内所有 analyze module 列表

// workspace 持久化(ADR-15 互通)
// <workspace>/<projectId>.xilink.analyze.json    // 单独文件存 analyze module 配置 · 不污染原 .xilink

② 收敛/成功判据

判据 阈值 含义
拖入 module 配置面板弹出 ≤ 100ms 显配置面板 UX 响应
配置保存到 workspace ≤ 500ms 落盘 + chain 图刷新显示新节点 状态一致性
同 chain log/RMS/scope name 唯一 重名拒接受配置(前端表单 inline 错) 数据回查可定位
移除 analyze module 不影响业务 chain original .xilink 文件不动 · 仅删 .xilink.analyze.json 中条目 关注点分离
analyze module 配置走 workspace 持久化 workspace 切换 → 重新打开同 .xilink → analyze module 配置自动恢复 多会话连续性
拖入 RMS / scope 自动弹出弹窗 session polling 启动后即弹 · 不需用户额外点击 立即看到效果

③ 失败回退路径(5 类)

失败 触发 UI 表现 恢复路径
name 重复 同 chain 第 2 个同名 module 表单 inline 错 "名字 'mic-tap-1' 已被使用" 用户改名 · 拒接受
afterNodeId 不存在(chain 节点已被业务流程删除) chain 状态变化后旧锚点无效 选 module 时显 "锚点节点不存在 · 已自动重定位 source-end" + warn 自动 fallback 到 source-end
.xilink.analyze.json 损坏 JSON parse 失败 warn "analyze 配置损坏 · 已重置" + workspace 内备份原文件到 .bak 用户重新拖入 module
ADR-17 F5 LogModuleService 未就位(超前派发本 fork) backend 报 503 toast "log module 服务未就位 · 请等 ADR-17 F5 完成" + 拒新建 等 F5 zombie 后自动恢复
配置面板字段验证失败(captureMode invalid 等) 表单 validate 失败 红框 + 字段 inline 错 用户修正字段

④ 用户操作流(5 步)

Step 操作 UI 反馈
1 链路图区右键 EQ 节点 → 弹出菜单 显 [+ log module / + RMS meter / + scope] 三 action
2 选 [+ log module] · 配置面板弹出(初始化 afterNodeId=eq-node) 面板含 name 输入 + position 锁 after-eq-node + captureMode 选 + textLog toggle
3 输入 name=after-eq-tap + captureMode=both + 勾文字 log → 点保存 配置保存 · chain 节点图新增 📡 log module 节点
4 再右键 mixer 节点 → [+ RMS meter] · 配置 name=mixer-rms + sourceEndpoint=log-module/after-eq-tap RMS meter 节点新增 + 配置保存
5 点 Top Bar [▶ Start Polling] session 启动 · RMS meter 弹窗自动浮出 · 显实时数值

⑤ 端到端真值 e2e(F4 必跑通)

test('XiLink-Analyze · 拖入 log module 配置 + 持久化', async ({ page }) => {
  await navigateToXilinkAnalyze(page)
  await selectXilink(page, 'ProjectA.xilink')

  // 右键 EQ 节点 · 拖入 log module
  await page.click('[data-test=chain-node-eq]', { button: 'right' })
  await page.click('text=+ log module')
  await page.fill('[data-test=module-config-name]', 'after-eq-tap')
  await page.selectOption('[data-test=module-config-capture-mode]', 'both')
  await page.click('[data-test=module-config-save]')

  // 验 chain 图新增节点
  await page.waitForSelector('[data-test=chain-node-log-module-after-eq-tap]')

  // 验 workspace 持久化文件
  const analyzeFile = await readWorkspaceFile('ProjectA.xilink.analyze.json')
  expect(analyzeFile.modules).toContainEqual(expect.objectContaining({ name: 'after-eq-tap' }))

  // 切到其他 stage 再切回 · 验配置恢复
  await navigateToRealtimeStage(page)
  await navigateToXilinkAnalyze(page)
  await selectXilink(page, 'ProjectA.xilink')
  await page.waitForSelector('[data-test=chain-node-log-module-after-eq-tap]')  // 仍在
})

test('XiLink-Analyze · 拖入 RMS meter · session 启动后自动弹窗', async ({ page }) => {
  await setupXilinkWithLogModule(page, 'mid-tap')
  await addAnalyzeModule(page, 'rms-meter-module', { name: 'rms-1', sourceEndpoint: 'log-module/mid-tap' })
  await page.click('[data-test=start-polling]')
  await page.waitForSelector('[data-test=rms-module-popup-rms-1]')  // 弹窗自动浮出
})

3.3 需求块 ③ · 滚动 polling 显示(类 QXDM)

[H2 patch · 2026-06-06] chain analysis plugin 已扩展为 4 类全探针(仅 input port,不 pass-through): - rms-meter-module:实时 RMS 电平探针 - scope-module:波形 scope 探针 - log-module:PCM 落盘 + 日志文本探针 - fft-module:FFT 频谱分析探针(算法实装留 ADR-18-R1+,UI 配置 + 占位接口已就绪)

4 类探针均通过 useXiLinkModuleTypes registry 统一注册,category='analysis',均遵循 ADR-17 §3.3 探针架构铁律(仅 input port)。

① 输入/输出契约

// Log Module Polling Panel 组件(P0)
interface LogModulePollingPanelProps {
  xilinkId: string
  logModuleName: string                                // 订阅的 log module
  pollingState: 'idle' | 'polling' | 'paused'
  autoScroll: boolean                                  // 自动滚动到底
  pcmWindowMs: number                                  // 时间轴 pcm 波形条窗口长度(默认 5000ms · 5s)
  textLogFilter?: { level?: 'info' | 'debug'; keyword?: string }
}

// 实时数据流(订阅 ADR-17 §3.3 WS frame log_module_v1)
interface LogModuleStreamFrame {
  v: 1
  type: 'log_module_v1'
  moduleName: string
  timestamp: number
  pcmChunk?: { sampleRate: number; channels: number; samples: Float32Array }
  textLog?: { level: string; message: string }
}

// 渲染目标
interface PollingPanelRenderState {
  pcmRingBuffer: Float32Array                          // 滚动 5s 波形 ring buffer
  textLogLines: { ts: number; level: string; message: string }[]   // 最近 1000 行
  framesPerSec: number                                 // 实时帧率显示(应 ≥ 10fps)
}

② 收敛/成功判据

判据 阈值 含义
polling 启动响应 ≤ 200ms 后 WS 订阅成功 + 首帧到达 启动响应
pcm 波形条滚动帧率 ≥ 10fps(对齐 ADR-17 §3.3 WS 帧率) 流畅滚动
文字 log 滚动 自动滚动到底 + 用户滚动时停自动 + 5s 不动后恢复 UX 类 QXDM
多 log module 并发显 ≥ 4 module 同时滚动不卡 · CPU < 30% 多检测点能力
时间轴 pcm 窗口同步 多 log module 时间轴严格对齐(同一 timestamp 锚) 跨节点对比
文字 log 过滤(level + keyword) 输入 keyword 后 100ms 内过滤生效 UX 即时性
暂停 / 恢复 / 快照 暂停后 ring buffer 不再追加 · 但订阅继续(数据丢但不卡)/ 快照保存当前 ring buffer 到 workspace snapshot/ UX 控制感

③ 失败回退路径(5 类)

失败 触发 UI 表现 恢复路径
WS 断连(后端崩 / 网络断) client → server WS 心跳超时 UI 显 "断连" · client 自动重连 5s 间隔 + 文字 log 加 "[disconnected]" 标记 重连后自动恢复 polling · ring buffer 数据缺口可视(空段)
帧到达速率突降(< 5fps) 后端负载高 / chain 性能差 UI 显 "polling 帧率低 · X fps" warn · 不停 用户可点 [⏸ Pause] 检查后端
pcm ring buffer 内存超限 用户开启 ≥ 8 module 同时高 fps + 长时窗口 warn "polling buffer 内存接近上限 · 已自动缩短窗口至 2s" + 自动减窗 用户关部分 module 或缩短窗口
文字 log 量爆炸(> 1000fps) 后端 textLog level=debug 全开 + 高频信号 warn "文字 log 速率过高 · 已自动切到 INFO only" + 强制过滤 用户调 textLog level 配置
快照保存失败(磁盘满) /snapshots/ 写盘失败 toast "快照保存失败 · 磁盘已满" + ring buffer 仍在内存可继续看 用户清盘后重试

④ 用户操作流(5 步)

Step 操作 UI 反馈
1 chain 内已有 log module mic-tap-1 + after-eq-tap · 点 Top Bar [▶ Start Polling] 启动 polling · WS 订阅 · Log Module Polling Panel 自动展开 · 两个 log module tab 显示
2 切到 mic-tap-1 tab · 看 pcm 波形条滚动(5s 时间窗 · 10fps) + 文字 log 滚动到底 实时显示
3 输入文字 log keyword "frame" · 列表自动过滤 100ms 内生效 UX 即时反馈
4 点 [⏸ Pause] · 波形条停 / 文字 log 不再滚动(但订阅仍在 · 累计帧丢可见) 暂停状态可见
5 点 [📸 Snapshot] · 当前 ring buffer + 文字 log 保存到 <workspace>/snapshots/<ts>/mic-tap-1.{pcm,log.txt} 快照保存成功 toast

⑤ 端到端真值 e2e(F4 必跑通)

test('XiLink-Analyze · 滚动 polling · pcm + 文字 log 实时显示', async ({ page }) => {
  await setupXilinkWithLogModule(page, 'mic-tap-1', { captureMode: 'both', textLogEnabled: true })
  await page.click('[data-test=start-polling]')

  // 等待 polling 稳定
  await page.waitForTimeout(3000)

  // 验帧率 ≥ 10fps
  const fps = parseFloat(await page.locator('[data-test=polling-fps-mic-tap-1]').textContent()!)
  expect(fps).toBeGreaterThanOrEqual(10)

  // 验 pcm 波形条已渲染
  const canvas = page.locator('[data-test=polling-pcm-canvas-mic-tap-1]')
  const isPcmRendered = await verifyCanvasNonEmpty(canvas)
  expect(isPcmRendered).toBe(true)

  // 验文字 log 至少 10 行
  const logLines = await page.locator('[data-test=polling-log-line]').count()
  expect(logLines).toBeGreaterThanOrEqual(10)
})

test('XiLink-Analyze · 暂停恢复 + 快照保存', async ({ page }) => {
  await startPolling(page, 'mic-tap-1')
  await page.waitForTimeout(2000)
  await page.click('[data-test=pause-polling]')

  // 暂停后帧不再增加(等 1s 验证)
  const linesBefore = await page.locator('[data-test=polling-log-line]').count()
  await page.waitForTimeout(1000)
  const linesAfter = await page.locator('[data-test=polling-log-line]').count()
  expect(linesAfter).toBe(linesBefore)  // 暂停 = 不更新 UI

  // 快照保存
  await page.click('[data-test=snapshot]')
  await page.waitForSelector('[data-test=snapshot-success-toast]')
  const snapshotFile = await glob(`${getWorkspaceDir()}/snapshots/*/mic-tap-1.pcm`)
  expect(snapshotFile.length).toBe(1)
})

F5 补充(2026-06-06)· 业务式 chain plugin module

H2(bb6b970)将 log/fft 注册到 useXiLinkModuleTypes(xilink-analyze 右键探针菜单),实测反馈路径不对。 正确形态:在 moduleLibrary.ts 注册为 IP 库模块(category: analysis),可从左侧 dock 拖入 xilink 主画布作为链路节点。 双击节点 → onOpenTuning analysis 分支 → usePopupModuleManager.open() → 对应 popup 浮窗。 数据流:popup 内 useAnalysisModulePolling(instanceId, type, ms) 定周期 GET /api/analysis/{nodeId}/{type}(F6 后端实装后生效)。


3.4 需求块 ④ · 快照对比 + 多工程切换

[H2 patch · 2026-06-06] WidgetEndpoint 候选扩展:fft-module/<id> 作为第 5 类候选(ADR-17 §3.4 共 4 类来源 + 本条第 5 类)。算法实装后可通过 fft_module_v1 WS 帧订阅实时频谱数据,留 ADR-18-R1+ 实装。

① 输入/输出契约

// Multi-View 状态(P0)
interface MultiViewState {
  enabled: boolean
  xilinkSlots: XiLinkSlot[]                            // 最多 4 个并排
  comparisonMode?: {
    type: 'overlay' | 'side-by-side'
    snapshotA: SnapshotRef                             // 快照 A
    snapshotB: SnapshotRef                             // 快照 B
    targetEndpoint: string                             // 对比目标(如 "log-module/mic-tap-1")
  }
}

interface XiLinkSlot {
  slotIndex: 0 | 1 | 2 | 3
  xilinkId: string
  pollingState: 'idle' | 'polling' | 'paused'
}

interface SnapshotRef {
  xilinkId: string
  capturedAt: number                                   // ms epoch
  filePath: string                                     // <workspace>/snapshots/<ts>/<modulename>.pcm
  metadata: {
    moduleNames: string[]
    durationMs: number
    rmsAvg?: number                                    // 元数据(若 RMS module 在场)
  }
}

// 后端 API(P5)
POST /api/xilink/snapshot                              // body: { xilinkId, modules } · 返回 SnapshotRef
GET  /api/xilink/snapshots                             // → SnapshotRef[](当前 workspace)
GET  /api/xilink/snapshots/:ts/:modulename             // → pcm + log 文件 stream(回放用)

② 收敛/成功判据

判据 阈值 含义
Multi-View 切换响应 ≤ 200ms 显第二 chain canvas UX 流畅
双 chain 独立 polling 各自 ≥ 10fps · 互不影响 并发能力
快照保存 < 1s 完成 metadata 写入 + pcm 文件 dump 用户感知"立即"
快照对比模式(overlay) RMS 双值同图叠加显示 · scope 双波形不同色重叠 可视化对比
同时打开 ≥ 4 工程 总 CPU < 50% · 内存 < 500MB 性能边界
工程切换时分析配置不丢 active xilink 切换到 X · 切回 Y · Y 的 analyze module 配置仍在 状态一致性
Multi-View 关闭 关闭后所有 slot 释放资源 · 内存回收 < 5s 资源管理

③ 失败回退路径(5 类)

失败 触发 UI 表现 恢复路径
Multi-View 第 N+1 工程开启失败(资源限) 已开 4 工程 + 新开 → 资源不足 warn "已达 Multi-View 最大数 4 · 关闭部分" + 拒新开 用户关部分
快照对比 endpoint 不匹配 snapshotA 来自 log/mic-tap-1 · snapshotB 来自 log/sink-tap(name 不同) warn "对比 endpoint 不一致 · 仅可视为不同 module" + fallback side-by-side 用户重选同名 endpoint 快照
快照文件丢失(用户手动删除 snapshots/ 目录) API GET /snapshots 返回空 toast "快照已丢失 · 列表已刷新" + 自动从内存 list 移除 用户重新拍快照
工程切换中 polling 残留 xilink A polling → 切 xilink B → A polling 未停 (内部) 自动停 A 的 polling + 释放 WS 订阅 自动 GC
Multi-View 布局保存失败(localStorage quota) 浏览器 localStorage 满 warn "布局保存失败 · 仅本会话有效" + 不阻塞功能 用户清浏览器缓存或忽略

④ 用户操作流(5 步)

Step 操作 UI 反馈
1 单 .xilink 工程 polling 中 · 拍快照 ProjectA-snapshot-14:00 快照保存成功
2 点 Top Bar 🗂 Multi-View → 弹出工程选择 → 加 ProjectB.xilink Multi-View 激活 · ProjectA + ProjectB chain 并排 · 各独立 [▶ Start]
3 ProjectB polling 中 · 拍快照 ProjectB-snapshot-14:30 快照保存成功
4 点 Top Bar [📊 对比模式] → 选 snapshotA=ProjectA-14:00 + snapshotB=ProjectB-14:30 + endpoint=log/mic-tap-1 RMS 双值同图叠加 · scope 双波形不同色重叠
5 切回单 view · ProjectA chain 切 ProjectC.xilink · ProjectA polling 自动停 资源释放 · UI 显 ProjectC chain

⑤ 端到端真值 e2e(F4 必跑通)

test('XiLink-Analyze · Multi-View 双工程并行 polling', async ({ page }) => {
  await setupXilinkWithLogModule(page, 'mic-tap-1')
  await navigateToXilinkAnalyze(page)
  await page.click('[data-test=multi-view-toggle]')
  await selectXilink(page, 'ProjectB.xilink', { multiView: true })

  // 两 slot 同时 polling
  await page.click('[data-test=multi-view-slot-0-start-polling]')
  await page.click('[data-test=multi-view-slot-1-start-polling]')
  await page.waitForTimeout(3000)

  // 验两 slot 各自独立 fps
  const fps0 = parseFloat(await page.locator('[data-test=polling-fps-slot-0]').textContent()!)
  const fps1 = parseFloat(await page.locator('[data-test=polling-fps-slot-1]').textContent()!)
  expect(fps0).toBeGreaterThanOrEqual(10)
  expect(fps1).toBeGreaterThanOrEqual(10)
})

test('XiLink-Analyze · 快照对比 overlay 模式', async ({ page }) => {
  // 拍两个快照
  await capturePollingSnapshot(page, 'ProjectA.xilink', 'mic-tap-1')
  await capturePollingSnapshot(page, 'ProjectB.xilink', 'mic-tap-1')

  // 进对比模式
  await page.click('[data-test=comparison-mode-toggle]')
  await selectSnapshot(page, 'A', 'ProjectA-mic-tap-1')
  await selectSnapshot(page, 'B', 'ProjectB-mic-tap-1')
  await page.selectOption('[data-test=comparison-mode-type]', 'overlay')

  // 验叠加视图
  await page.waitForSelector('[data-test=comparison-overlay-canvas]')
  const overlayLines = await page.locator('[data-test=comparison-line]').count()
  expect(overlayLines).toBe(2)  // A + B 两条线
})
  • FftModulePopup.vue: ECharts log freq 轴,600×320,通道颜色可配,useAnalysisModulePolling

4. 实施清单(Implementation · v1.9 fork 表 · 4 fork)

✅ v0.1 业务契约已填齐 · 4 fork ready 等 ADR-17 F5 zombie 后派发 · UID 锁定 v1.9 命名铁律 P{N}.A18.F{M}-<slug>

F# UID 部门 CPU 工作量 描述
F1 P5.A18.F1-xilink-workspace-readonly-api 后端 ClaudeB 1.5d NEW WorkspaceXilinkAnalyzeService · GET /api/workspace/xilinks(列表 + hasLogModules 标记)+ GET /:id/chain(read-only)+ POST /:id/activate-for-analyze(workspace 持久化 lastAnalyzedXilinkId)+ POST /api/xilink/snapshot(快照保存 metadata + pcm dump 协调)+ GET /api/xilink/snapshots(快照列表)· 业务契约 §3.1 + §3.4 ①
F2 P0.A18.F2-xilink-analyze-stage-ui 前端 ClaudeA 2.5d XiStudioShell.vue 加 [🔬 XiLink-Analyze Stage] button · 新建 stages/xilink-analyze/ stage · Top Bar(workspace + active xilink 下拉 + Start/Pause/Snapshot/Multi-View 控件)+ Center 链路图区(复用 ADR-08 ChainCanvas + ADR-16 SubgraphRuntime read-only)+ chain 节点右键菜单 + AnalyzeModuleConfigPanel(三 module type 配置面板)+ Bottom log file browser · 业务契约 §3.1 + §3.2 操作流 ⭐ 关键路径
F3 P0.A18.F3-polling-display-and-popups 前端 ClaudeA 1.5d LogModulePollingPanel(pcm 波形条 ring buffer + 文字 log 滚动区 + 多 log module tab 切换)+ RMS Meter Popup component(对接 ADR-17 §3.4 RMSMeterModuleConfig)+ Scope Popup component(对接 ScopeModuleConfig)+ Multi-View 状态机 + 快照对比 overlay/side-by-side · 业务契约 §3.3 + §3.4 操作流
F4 P_e2e.A18.F4-truth-e2e-xilink-analyze 测试编排 ClaudeC 1.5d playwright e2e XiLink-Analyze stage 全矩阵真值:§3.1-§3.4 4 块业务契约 ⑤ 段 e2e 全跑(.xilink 加载 + 拖入 module + 滚动 polling + 多工程对比)+ workspace 持久化回归 + Multi-View 资源管理回归 · 复用 ADR-17 F7 e2e harness
合计 7d ≈ 1-1.5 周(4 fork 跨栈)

关键路径:F1 → F2 → F3 → F4(后端 API → stage UI 主框架 → polling/弹窗显 → e2e) Phase 派发计划(等 ADR-17 F5 zombie 后才派 · 不允许超前): - Phase 1(后端就位):F1(ClaudeB 1.5d) - Phase 2(前端主框架 + polling):F2(ClaudeA 2.5d) → F3(ClaudeA 1.5d)串行(同一 ClaudeA) - Phase 3(全矩阵 e2e 收尾):F4(ClaudeC 1.5d 🏆)


5. 与现有 ADR 关系

ADR 关系
ADR-AIOS-17(XiStudio Realtime IO Architecture v2) 硬依赖底座 · 本 ADR 复用 §3.3 LogModuleConfig + DSP 落盘协议 + WS frame log_module_v1 + §3.4 RMSMeterModuleConfig + ScopeModuleConfig + 弹窗 component schema · 本 ADR 不重复造 schema · F2 必须等 ADR-17 F5(P5.A17.F5-log-module-and-display-module-infra)zombie 后才能开始
ADR-AIOS-08(XiLink Stage UX · accepted) read-only 复用 · 本 ADR F2 链路图渲染区直接复用 ChainCanvas 组件 · 仅 read-only 模式 · 不动 chain 编辑
ADR-AIOS-08-R1 · ADR-AIOS-16(主图子图统一架构 · proposed) 互通 · 本 ADR 链路图渲染调用 SubgraphRuntime 扁平化展开(若 ADR-16 accepted)· 否则 fallback 直接渲染 chain.nodes
ADR-AIOS-12(XiTest Realtime Arch · 7 widget 业务契约 · fulfilled 🏆) 正交 · 本 ADR 不显 7 widget · 不动 MeterTapService 30fps 老路径 · 仅复用 §3 业务契约填齐格式标杆
ADR-AIOS-11(XiTune) 互通 · WidgetEndpoint 第 4 类 xitune-module/<id>/<port> 来源 · 但本 stage 不直接显 widget · 仅 RMS / scope module 内部 sourceEndpoint 引用
ADR-AIOS-15(workspace persistence · accepted+impl) 互通 · log module / RMS / scope module 配置 + Multi-View 布局 + 快照 metadata 走 workspace 持久化(<workspace>/<projectId>.xilink.analyze.json + <workspace>/snapshots/)
ADR-AIOS-13(deferred-by-ADR-17) 无直接关系 · 但 ADR-13 H1+H2+H3 hotfix 触底教训驱动 ADR-17 + ADR-18 双 stage 拆分

6. 风险与缓解

风险 缓解
ADR-17 F5 未就位时本 ADR fork 派发会卡(LogModuleService schema 缺失) 硬依赖标记 · 本 ADR §4 Phase 派发计划明确"等 ADR-17 F5 zombie 后才派 · 不允许超前" · DASHBOARD §🤔 持续监控 F5 状态
多 .xilink 同时 polling 性能压力(后端 BuiltinLinkRegistry chain 性能 + 前端渲染压力) F1+F3 性能基线测试 · 4 工程并发 ≤ 4 module 各自 ≥ 10fps · 前端 ring buffer 自动减窗保护
ChainCanvas 组件复用代价(原本是编辑模式 · 改 read-only 需加 prop) F2 ClaudeA 与 ADR-08 既有代码协调 · 加 readonly: true prop 走最小修改路径 · 不破坏 ADR-08 chain 编辑能力
ADR-16 SubgraphRuntime 若未 accept · 链路图渲染降级 F2 fallback 到 ADR-08 chain 编辑器原始扁平节点列表 · 不阻塞 stage 落地
快照对比 overlay 视觉效果(双线叠加色重) F3 ClaudeA UX 设计 · 用 design-token accent + accent2 双色 + 半透明 · 必要时切 side-by-side
与 ADR-17 fork 派发竞争 ClaudeA/B 资源 本 ADR 实施期晚于 ADR-17(F5 zombie 后)· ClaudeA 排队顺序:ADR-17 F2/F4/F6(共 5d)→ 本 ADR F2/F3(共 4d)· ClaudeB 排队:ADR-17 F1/F3/F5(共 6.5d)→ 本 ADR F1(1.5d)

7. 状态流转

时间 事件 备注
2026-06-04 15:18 (前置)ADR-AIOS-17 v0.2 accepted 用户拍板选项 B 拆双 ADR · 本 ADR 范围确定
2026-06-04 15:51 proposed v0.1 Cline-AIOS 一次性起草完整版 · §3 业务契约 5 段 × 4 块全填齐 · 4 fork 实施清单
2026-06-04 16:14 accepted v0.1 用户拍板路径 A(4 任务并发起手):accept ADR-18 + start ADR-15 F8 + start ADR-17 F1+F2 + start ADR-18 F1 · F1 立即派发(不依赖 ADR-17 F5)
2026-06-04 16:14 impl Phase 1 起手 F1(P5.A18.F1-xilink-workspace-readonly-api · ClaudeB 1.5d · 后端 read-only API + 快照 API · 与 ADR-17 F1 串联派)
(待) impl Phase 2 F2(stage UI 主框架 + chain 节点拖入 · ClaudeA 2.5d) → F3(polling 显示 + 弹窗 · ClaudeA 1.5d)串行
(待) impl Phase 3 F4(e2e 全矩阵真值 · ClaudeC 1.5d 🏆)
(待) fulfilled 🏆 4 fork 全 zombie · §3.1-§3.4 4 块业务契约 ⑤ 段 e2e 全通过 · 用户验收

8. 决议者签名

  • proposed by:Cline-AIOS(2026-06-04 15:51 · v0.1 一次性起草 · 借 ADR-17 v0.2 上下文热度避免二次返工)
  • review by:user(2026-06-04 16:14 · 拍板路径 A 4 任务并发起手 · 一次性 review + accept)
  • accepted by:user(2026-06-04 16:14)
  • 后续修订:ADR-AIOS-18-R1(若中途需求漂移 · 如多工程并发数从 4 调到 ≥ 6 等)

下一步:用户 review → accept ADR-AIOS-18 → Cline-AIOS 同步 DASHBOARD v4.0.10 → v4.0.11(ADR-18 加 §📋 4 fork blocked-by-ADR-17-F5 + §推荐新派 + §📅 历史)+ P_arch/PROCESS.md v2.18(加 ADR-18 子事件状态段)→ 等 ADR-17 F5 zombie 后派 ADR-18 F1(ClaudeB 1.5d 关键路径起手)。