P0.UA13-input-output-device-config-panel · ADR-13 LeftDock § Engine 扩展(议题 1+2 device 选择 UI)
Worker:ClaudeA · 前端 / 预计:1.0d / 优先级:P1 / 状态:dispatched 隔离:🧵 文件隔离(同 worktree 同 branch · 与 P0.UH12 + P1.A14.F4-module-library + ADR-14 active 文件路径完全正交)
🔍 触发与解锁链
| 触发 | 状态 | 影响 |
|---|---|---|
| ADR-AIOS-13 v1.0 accepted(2026-06-01 19:21) | ✅ user accept | 8 fork ready · Phase 2 fork 5 |
fork 1 P5.UA13-realtime-session-service zombie(92445f5) |
✅ 后端 API + WS 已就位 | 本 fork 调 /api/audio/devices/{inputs,outputs} + 填 store 字段 |
fork 2 P5.UA13-builtin-link-registry zombie(d8f0677) |
✅ BuiltinLinkRegistry + realtime-loopback.json frozen | loopback mode 加载 builtin link |
fork 4 P0.UA13-realtime-run-store-toolbar zombie(0111d16) |
✅ realtimeRunStore Pinia 7 字段 + 4 actions | 本 fork 双向绑定 inputDeviceId/outputDeviceId/signalConfig |
| 用户 6/2 11:21 双 stop fork 2+4 解锁本 fork(blocked → 可派) | ✅ Phase 2 fork 5 可派 | dispatched 11:50 |
→ 本 fork zombie 解锁:Phase 3 fork 8 P_e2e.UA13-truth(议题 1 + 议题 2 端到端 e2e · 需 selectInputDevice/selectOutputDevice/selectSignal UI 支撑)。
任务定义(基于 ADR-AIOS-13 v1.0 §2.5 + §3.1 ① + §3.2 ①)
扩展 LeftDock § Engine 段(stages/xitest/drawers/EnginePanel.vue · ADR-12 §5.3 b4a8ea2 已落 § Engine + Session + Snapshots 三段)· 追加 § Device + § Signal Config 两段 · 完成议题 1 + 议题 2 device 选择 UI 闭环。
核心范围:
1. types schema(types/audio-device.ts):InputDevice + OutputDevice + SignalConfig + AudioDevicesError schema(对齐 fork 1 后端 schema · 复用 ADR-13 §2.4 + §3.2 ① signalConfig)
2. useAudioDevices composable(composables/useAudioDevices.ts 新建):loadInputs() GET /api/audio/devices/inputs · loadOutputs() GET /api/audio/devices/outputs · refresh() · structured error · 模块级缓存(避免 panel 反复挂载重复请求)
3. EnginePanel.vue 扩展(行号正交 · 仅在末尾追加 § Device + § Signal Config 段 · 不动 § Engine + Session + Snapshots):
- § Device 段:Input Device 下拉(必填) + Output Device 下拉(可选) + 错误显示 + Refresh 按钮
- mode auto-detect:<div class="xy-mode-badge"> computed:仅 input → "hardware-direct" · input + output → "loopback" · 显示在段顶部
- § Signal Config 段:仅 loopback mode 可见 · type 下拉(sine/pink/mls/multitone/sweep)+ frequency 数字输入(仅 sine 可见)+ amplitude_dbfs 数字输入(默认 -20)+ duration 数字输入
4. 双向绑定 realtimeRunStore:UI 选择 → realtimeRunStore.inputDeviceId / outputDeviceId / signalConfig · store 字段更新 → UI 显示同步(fork 4 store 已落 · 本 fork 仅消费)
5. vitest 单测:useAudioDevices(load/error/cache/refresh ≥ 5 用例)+ EnginePanel(device 渲染 + auto-detect + signalConfig 条件显示 + store 双向绑定 ≥ 5 用例)
6. Ximind 兼容性 5 项(ADR §11):AudioDevice schema 含 description 字段(自然语言)+ camelCase + structured error(code+message+recoveryHints)+ 大模型可读 device 列表 + signalConfig 含 type 语义
不在本 fork 范围: - ❌ 不实施顶栏 ▶/■(已 fork 4 落) - ❌ 不实施 capture/recapture 弹框(留 fork 7) - ❌ 不动 realtimeRunStore.ts(本 fork 仅消费 · 不改 schema) - ❌ 不动后端 API(fork 1+2 已落) - ❌ 不实施真硬件联调 e2e(留 fork 8) - ❌ 不动 ADR-12 §3 7 类 MeasurementNode 业务行为契约 / 不动 § Engine + Session + Snapshots 三段(b4a8ea2 已落 · 仅末尾追加)
完整 prompt(直接复制粘贴 worker 终端)
[U-thread] P0.UA13-input-output-device-config-panel
[部门] 前端 P0-xishell · 推荐 skill: vuejs-typescript-best-practices
[Worker CWD] d:/work/25_claude/workspace/AlgoDepartment/04_development/
[Occupies] P0.K-xitest-engine-panel(写 · EnginePanel.vue § Device + § Signal Config 末尾段追加)
[隔离] 🧵 文件隔离 · 仅写:
- frontend_vue3/src/types/audio-device.ts(新建/扩展 · InputDevice + OutputDevice + SignalConfig + AudioDevicesError)
- frontend_vue3/src/composables/useAudioDevices.ts(新建 · GET /api/audio/devices/{inputs,outputs} · 缓存 + 错误结构化)
- frontend_vue3/src/stages/xitest/drawers/EnginePanel.vue(扩展 · 仅末尾追加 § Device + § Signal Config 两段 · 不动 § Engine + Session + Snapshots b4a8ea2 已落)
- frontend_vue3/src/composables/__tests__/useAudioDevices.spec.ts(新建 vitest)
- frontend_vue3/src/stages/xitest/drawers/__tests__/EnginePanel.spec.ts(新建/扩展 vitest)
与 P0.UH12(同 stages/xitest/ 但 BottomDock + tab 体系 行号正交)+ P1.A14.F4-module-library(components/widget-library/)+ ADR-14 4 active 文件路径完全正交
[优先级] P1 · 1.0d · Phase 2 关键路径(解锁 fork 8 e2e)
[ADR] d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/ADR/ADR-AIOS-13-xitest-realtime-dual-mode.md
(必读 §2.5 LeftDock § Engine 扩展段 + §3.1 ① 议题 1 hardware mode device 契约 + §3.2 ① 议题 2 loopback mode signalConfig 契约 + §11 Ximind 兼容性)
[业务行为契约引用]
ADR-13 §3.1 ①(议题 1 hardware mode):POST /api/realtime/start body 含 mode="hardware" + inputDeviceId
ADR-13 §3.1 ④ Step 2-3:LeftDock § Engine 选 Input Device → 系统自动识别 = 物理设备 → mode auto-detect = "hardware-direct"
ADR-13 §3.2 ①(议题 2 loopback mode):POST /api/realtime/start body 含 mode="loopback" + inputDeviceId + outputDeviceId + signalConfig{ type, frequency?, amplitude_dbfs }
ADR-13 §3.2 ④ Step 1-3:选 input + output + 信号源 sine 1kHz -20dBFS · 系统识别 loopback mode
ADR-13 §3.2 ③ 失败回退:input === output 红警 "Loopback requires distinct input/output device"
[参考文档](绝对路径 · worker 必读)
- ADR-13:d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/ADR/ADR-AIOS-13-xitest-realtime-dual-mode.md
· §2.5 LeftDock § Engine 扩展段(line 316:沿用 b4a8ea2 三段 + 新增 input/output device 选择 + mode auto-detect)
· §3.1 ①+④:hardware mode 输入契约 + 用户操作流(本 fork 实施 Step 2-3)
· §3.2 ①+④:loopback mode 输入契约 + 用户操作流(本 fork 实施 Step 1-3)
· §11.1+§11.2 Ximind:AudioDevice description + signal type 语义 + structured error
- 范式参考(已 zombie · ClaudeA 同部门标本):
· d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/prompts/done/ADR-AIOS-12/P0.U-engine-session-snapshots--b4a8ea2.md
(⭐ EnginePanel.vue § Engine + Session + Snapshots 三段范式 · 本 fork 追加 § Device + § Signal Config 两段 · 严格沿用其 design-token + 响应式横竖屏)
· d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/prompts/done/ADR-AIOS-13/P0.UA13-realtime-run-store-toolbar--0111d16.md
(⭐ realtimeRunStore 7 字段 + 4 actions + composition API + fetch + WS 范式 · 本 fork 双向绑定 store)
- parent zombie(必读 · 本 fork 调用其 API):
· d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/prompts/done/ADR-AIOS-13/P5.UA13-realtime-session-service--92445f5.md
(后端 RealtimeSessionService · /api/audio/devices/{inputs,outputs} · POST /api/realtime/start request body schema)
· d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/prompts/done/ADR-AIOS-13/P5.UA13-builtin-link-registry--d8f0677.md
(BuiltinLinkRegistry + realtime-loopback.json frozen · GET /api/builtin-links/realtime-loopback)
- 现状必读(主仓真值核查 · Step 1 必跑):
· frontend_vue3/src/stages/xitest/drawers/EnginePanel.vue(全文必读 · ⭐ 看 § Engine + Session + Snapshots 三段当前结构 · 末尾追加位置 · template + script + style 范式)
· frontend_vue3/src/stores/realtimeRunStore.ts(全文必读 · ⭐ 看 7 state 字段:isRunning/activeMode/inputDeviceId/outputDeviceId/signalConfig/error/wsConnected · 双向绑定目标)
· frontend_vue3/src/composables/(看 useAudioEngine 或类似 composable 范式 · 决定 useAudioDevices 风格)
· 后端 backend_csharp/Routes/AudioDeviceRoutes.cs 或 Routes/RealtimeApiRoutes.cs(grep 'devices/inputs' · 确认实际 endpoint 路径 + response schema · 字段名 camelCase)
【背景】
ADR-AIOS-13 v1.0 accepted(2026-06-01 19:21)。议题 1 hardware mode + 议题 2 loopback mode 业务行为契约 ① 输入端:都需要前端 LeftDock § Engine 段提供 input device 选择(hardware/loopback 都需)+ output device 选择(loopback 需)+ signalConfig(loopback 需)+ mode auto-detect 显示。
依赖现状:
- fork 1 P5.UA13-realtime-session-service zombie(`92445f5` · 后端 API + WS 完整 · /api/audio/devices/{inputs,outputs} 已就位)
- fork 2 P5.UA13-builtin-link-registry zombie(`d8f0677` · realtime-loopback.json frozen + /api/builtin-links/* · loopback 模式可读取)
- fork 4 P0.UA13-realtime-run-store-toolbar zombie(`0111d16` · realtimeRunStore Pinia 7 state + 4 actions 完整)
- 范式参考 b4a8ea2 已落 EnginePanel.vue § Engine + Session + Snapshots 三段 · 本 fork 追加两段
3 路真值核查锚点(Step 1 必跑):
① EnginePanel.vue 现有结构(b4a8ea2):看末尾追加位置 + template/script/style 风格 + design-token 用法
② realtimeRunStore.ts(0111d16):7 state 字段精确名 + signalConfig 字段类型 · 本 fork 双向绑定不能错
③ /api/audio/devices/{inputs,outputs} 后端 endpoint(grep backend_csharp/Routes/* · 字段 camelCase)+ AudioDevice response schema(deviceId / friendlyName / channels / sampleRate / description?)
本 fork 核心:扩展 EnginePanel.vue 末尾 + 落 useAudioDevices composable + types schema + vitest · 0 破坏 b4a8ea2 三段已落业务 · 0 改 fork 4 store schema · 0 改后端。
【执行步骤】(7 步 · 总 1.0d)
Step 1 · 真值核查 + git pull(必跑 · 0.1d)
- git status / git pull origin xistudio --no-rebase
- cat frontend_vue3/src/stages/xitest/drawers/EnginePanel.vue(全文 · 输出:① § Engine 段当前结构 ② § Session 段 ③ § Snapshots 段末尾位置 ④ template 整体框架 ⑤ script setup 风格(composition API)⑥ design-token 用法实例)
- cat frontend_vue3/src/stores/realtimeRunStore.ts(全文 · 输出:① 7 state 字段精确名 ② signalConfig 字段 type 接口 ③ activeMode union literal type)
- grep -r 'audio/devices' backend_csharp/Routes/(输出:① 实际 endpoint 路径 ② AudioDevice response schema 字段名 ③ 是否已有 InputDevice/OutputDevice 区分 type)
- cat frontend_vue3/src/composables/useAudioEngine.ts 或类似(若存在 · 看 composable 风格)
- 输出真值核查到 commit message body:
· EnginePanel.vue 现有段 = ???
· 末尾追加位置 = line ???
· realtimeRunStore.ts signalConfig type = ???
· /api/audio/devices/{inputs,outputs} schema = ???
· 后端 InputDevice / OutputDevice 区分 = ???
Step 2 · 落 types/audio-device.ts(0.15d)
- 抄 ADR §3.1 ① + §3.2 ① · 对齐后端 schema:
export interface AudioDevice {
deviceId: string
friendlyName: string
channels: number // device 总通道数
sampleRates: number[] // 支持的采样率 [44100, 48000, 96000]
description?: string // ⭐ Ximind 自然语言描述
}
export interface InputDevice extends AudioDevice {
kind: 'input' // discriminated union
isLoopbackCapable?: boolean // XiAudioLoopback 标记
}
export interface OutputDevice extends AudioDevice {
kind: 'output'
}
export type AnyAudioDevice = InputDevice | OutputDevice
// ADR §3.2 ①:loopback mode 的 signalConfig
export type SignalType = 'sine' | 'pink' | 'mls' | 'multitone' | 'sweep'
export interface SignalConfig {
type: SignalType
frequency?: number // sine 才有
amplitudeDbfs: number // 默认 -20
duration?: number // ms · sweep 才有
description?: string // ⭐ Ximind
}
// 错误结构化
export interface AudioDevicesError {
code: 'DEVICES_LOAD_FAILED' | 'DEVICE_NOT_FOUND' | 'NETWORK_ERROR' | string
message: string
recoveryHints: string[]
}
Step 3 · 落 useAudioDevices composable(0.15d)
- frontend_vue3/src/composables/useAudioDevices.ts(新建):
import { ref, computed } from 'vue'
import type { InputDevice, OutputDevice, AudioDevicesError } from '@/types/audio-device'
// 模块级缓存(panel 反复挂载不重复请求)
const cache = {
inputs: ref<InputDevice[] | null>(null),
outputs: ref<OutputDevice[] | null>(null),
error: ref<AudioDevicesError | null>(null),
loading: ref(false),
}
export function useAudioDevices() {
async function loadInputs(force = false): Promise<InputDevice[]> {
if (!force && cache.inputs.value) return cache.inputs.value
cache.loading.value = true
try {
const res = await fetch('/api/audio/devices/inputs')
if (!res.ok) throw new Error(`HTTP ${res.status}`)
const data = await res.json()
cache.inputs.value = data
cache.error.value = null
return data
} catch (e) {
cache.error.value = {
code: 'DEVICES_LOAD_FAILED',
message: 'Failed to load input devices',
recoveryHints: ['Check backend connection', 'Click Refresh to retry'],
}
throw e
} finally {
cache.loading.value = false
}
}
async function loadOutputs(force = false): Promise<OutputDevice[]> { /* 同上 · /api/audio/devices/outputs */ }
async function refresh(): Promise<void> {
await Promise.all([loadInputs(true), loadOutputs(true)])
}
return {
inputs: computed(() => cache.inputs.value ?? []),
outputs: computed(() => cache.outputs.value ?? []),
error: computed(() => cache.error.value),
loading: computed(() => cache.loading.value),
loadInputs,
loadOutputs,
refresh,
}
}
- 关键点:
· 模块级缓存 · panel 多实例共享
· structured error · code + message + recoveryHints[](Ximind)
· refresh() force 刷新 · 用户手动点 Refresh 按钮触发
· endpoint 路径严格对齐 Step 1 真值核查结果
Step 4 · 扩展 EnginePanel.vue 末尾追加 § Device + § Signal Config(0.35d)
- 仅在 b4a8ea2 已落 § Snapshots 段之后追加 · 严禁动 § Engine + Session + Snapshots 任何一行
- § Device 段(template):
<section class="xy-engine-section">
<h3>Device</h3>
<div class="xy-mode-badge" :data-mode="activeMode">{{ modeLabel }}</div>
<label>Input Device</label>
<select v-model="store.inputDeviceId">
<option :value="null">— Select —</option>
<option v-for="d in inputs" :key="d.deviceId" :value="d.deviceId">{{ d.friendlyName }}</option>
</select>
<label>Output Device <span class="xy-hint">(optional · loopback only)</span></label>
<select v-model="store.outputDeviceId">
<option :value="null">— None (hardware mode) —</option>
<option v-for="d in outputs" :key="d.deviceId" :value="d.deviceId">{{ d.friendlyName }}</option>
</select>
<button @click="refresh" :disabled="loading">Refresh</button>
<p v-if="loopbackError" class="xy-error">⚠️ Loopback requires distinct input/output device</p>
<p v-if="error" class="xy-error">⚠️ {{ error.message }}<br>{{ error.recoveryHints.join(' · ') }}</p>
</section>
- § Signal Config 段(仅 loopback mode 可见):
<section class="xy-engine-section" v-if="activeMode === 'loopback'">
<h3>Signal Config</h3>
<label>Type</label>
<select v-model="store.signalConfig.type">
<option value="sine">Sine</option><option value="pink">Pink Noise</option>
<option value="mls">MLS</option><option value="multitone">Multitone (WAV)</option><option value="sweep">Sweep</option>
</select>
<label v-if="store.signalConfig.type === 'sine'">Frequency (Hz)</label>
<input v-if="store.signalConfig.type === 'sine'" type="number" v-model.number="store.signalConfig.frequency" min="20" max="20000" step="1" />
<label>Amplitude (dBFS)</label>
<input type="number" v-model.number="store.signalConfig.amplitudeDbfs" min="-60" max="0" step="0.1" />
</section>
- script setup:
import { computed, watchEffect } from 'vue'
import { useRealtimeRunStore } from '@/stores/realtimeRunStore'
import { useAudioDevices } from '@/composables/useAudioDevices'
const store = useRealtimeRunStore()
const { inputs, outputs, error, loading, refresh, loadInputs, loadOutputs } = useAudioDevices()
// mode auto-detect(ADR §3.1 ④ Step 3 + §3.2 ④ Step 2)
const activeMode = computed(() => {
if (!store.inputDeviceId) return null
if (store.outputDeviceId) return 'loopback'
return 'hardware'
})
const modeLabel = computed(() => {
if (activeMode.value === 'hardware') return 'Mode: Hardware Direct'
if (activeMode.value === 'loopback') return 'Mode: Loopback'
return 'Mode: — (select input device)'
})
// ADR §3.2 ③ failure: input === output
const loopbackError = computed(() =>
activeMode.value === 'loopback' && store.inputDeviceId === store.outputDeviceId
)
// 同步 store.activeMode(可选 · 让 store 知道当前 panel 推导的 mode)
watchEffect(() => { if (activeMode.value) store.activeMode = activeMode.value })
// signalConfig 默认值(loopback mode 切入时初始化)
watchEffect(() => {
if (activeMode.value === 'loopback' && !store.signalConfig) {
store.signalConfig = { type: 'sine', frequency: 1000, amplitudeDbfs: -20 }
}
})
onMounted(() => {
loadInputs().catch(() => {})
loadOutputs().catch(() => {})
})
- style:沿用 b4a8ea2 design-token · CSS class 复用 .xy-engine-section / .xy-error / .xy-hint / .xy-mode-badge(可能需新增) · 严禁硬编码 hex
⚠️ 行号正交:仅末尾追加 + script setup 内的 import + computed + onMounted 段 · 不动 b4a8ea2 已落三段任何代码
Step 5 · vitest 单测(0.2d)
- frontend_vue3/src/composables/__tests__/useAudioDevices.spec.ts(新建):
· loadInputs 成功 · 缓存命中第二次不重新请求
· loadInputs 失败 · error structured(code/message/recoveryHints)
· loadOutputs 同上
· refresh force 刷新 · 重新请求
· 多实例共享缓存(模块级状态)
· ≥ 5 用例
- frontend_vue3/src/stages/xitest/drawers/__tests__/EnginePanel.spec.ts(新建/扩展):
· § Device 段渲染 input/output 下拉
· 选 input → store.inputDeviceId 更新(双向绑定)
· 选 input → activeMode = 'hardware' · modeLabel 显示
· 选 input + output → activeMode = 'loopback' · § Signal Config 显示
· input === output → loopbackError 显示
· § Signal Config sine 类型显示 frequency 输入 · 其他类型隐藏
· ≥ 5 用例
- mock fetch:vi.spyOn(global, 'fetch') · mock realtimeRunStore:createPinia + setActivePinia
- 期望 +10~15 用例
Step 6 · build + typecheck + 浏览器手动验证(0.1d)
- cd frontend_vue3
- npm run typecheck(零错误)
- npm run test:unit(基线 +10~15)
- 浏览器手动:
· npm run dev + 启后端
· 打开 xitest stage · 切到 LeftDock § Engine drawer
· 滚到末尾看到 § Device + § Signal Config 段(b4a8ea2 三段在前 · 不能丢)
· 选 Input Device(下拉应该列出后端真实设备)· mode badge → "Hardware Direct"
· 加选 Output Device · mode badge → "Loopback" · § Signal Config 段出现
· § Signal Config:type=sine · frequency=1000 · amplitude_dbfs=-20(默认)
· 切 type 到 pink · frequency 输入隐藏
· input === output(选同一个 device)· loopbackError 红警显示
· DevTools Network → /api/audio/devices/inputs + /outputs 200
· 点 Refresh → 二次 fetch
· 关后端 → error structured 显示("Check backend connection" recoveryHints)
- ⚠️ 真硬件测量联调留 fork 8 e2e
Step 7 · commit + push(0.1d)
- git add frontend_vue3/src/types/audio-device.ts frontend_vue3/src/composables/useAudioDevices.ts frontend_vue3/src/stages/xitest/drawers/EnginePanel.vue frontend_vue3/src/composables/__tests__/useAudioDevices.spec.ts frontend_vue3/src/stages/xitest/drawers/__tests__/EnginePanel.spec.ts
- git commit(三元组 trailer 见下) · git push origin xistudio
【验收】
形式合规:
- [ ] npm run typecheck 零错误
- [ ] npm run test:unit 全绿(基线 +10~15)
- [ ] 仅 5 文件修改/新建(1 types + 1 composable + 1 stage 扩展 + 2 测试)
- [ ] 不动 b4a8ea2 已落 EnginePanel.vue § Engine + Session + Snapshots 三段
- [ ] 不动 0111d16 realtimeRunStore.ts schema(本 fork 仅消费)
- [ ] 不动后端 / contracts / dsp_algo
- [ ] 不引入新 NPM 依赖(fetch 原生 + Pinia + Vue 内置)
- [ ] § Signal Config 段 v-if="activeMode === 'loopback'" 严守(hardware mode 不可见)
业务行为契约(ADR §3.1 ① + §3.2 ①):
- [ ] §3.1 ①:hardware mode · 选 input device → store.inputDeviceId 填入(顶栏 ▶ 调 start 时 mode='hardware')
- [ ] §3.1 ④ Step 2-3:LeftDock § Engine 选 Input Device → 系统自动识别 = "hardware-direct"(modeLabel 显示)
- [ ] §3.2 ①:loopback mode · 选 input + output + signalConfig → store 全字段填入(顶栏 ▶ 调 start 时 mode='loopback' 完整 payload)
- [ ] §3.2 ④ Step 1-3:选 input + output + signal sine 1kHz -20dBFS → store.signalConfig 完整
- [ ] §3.2 ③:input === output → loopbackError 红警显示(Loopback requires distinct input/output device)
- [ ] mode auto-detect:仅 input → "hardware" · input + output → "loopback" · 切换响应式
Ximind 兼容性(ADR §11):
- [ ] AudioDevice schema 含 description 字段(自然语言)
- [ ] 字段名 camelCase(deviceId / friendlyName / sampleRates / amplitudeDbfs · 无缩写歧义)
- [ ] AudioDevicesError structured(code + message + recoveryHints[])
- [ ] SignalType union 含语义(sine/pink/mls/multitone/sweep · 大模型可解释)
- [ ] modeLabel 含自然语言("Hardware Direct" / "Loopback" / "select input device")
【commit】
subject:`feat(P0.UA13-input-output-device-config-panel): LeftDock § Engine 扩展 § Device + § Signal Config + mode auto-detect · ADR-13 §2.5 §3.1 §3.2 §11`
trailer(必须精确 · 三元组):
[step=7/7] [pid=P0] [uid=UA13-input-output-device-config-panel] [occupies=P0.K-xitest-engine-panel]
[files=frontend_vue3/src/types/audio-device.ts, frontend_vue3/src/composables/useAudioDevices.ts, frontend_vue3/src/stages/xitest/drawers/EnginePanel.vue, frontend_vue3/src/composables/__tests__/useAudioDevices.spec.ts, frontend_vue3/src/stages/xitest/drawers/__tests__/EnginePanel.spec.ts]
[isolation] file(同 worktree 同 branch · 与 P0.UH12 + P1.A14.F4-module-library + ADR-14 active 文件路径完全正交)
[derived_from] ADR-AIOS-13 v1.0 §2.5 + §3.1 + §3.2 + §11 · parent zombie 92445f5(fork 1 RealtimeSessionService API)+ d8f0677(fork 2 BuiltinLinkRegistry)+ 0111d16(fork 4 realtimeRunStore)· 范式 b4a8ea2(EnginePanel 三段)
[truth-check] EnginePanel 现有段=??? · realtimeRunStore signalConfig type=??? · /api/audio/devices schema=???(Step 1 输出)
[acceptance] § Device + § Signal Config 末尾追加 · mode auto-detect computed · loopback v-if · loopbackError 红警 · vitest +10~15 全绿 · typecheck 0 错
[ximind] 5 项检查清单全过(camelCase + AudioDevice description + SignalType 语义 + AudioDevicesError structured + modeLabel 自然语言)
【禁止】(7 条红线)
1. ❌ 不动 b4a8ea2 已落 EnginePanel.vue § Engine + Session + Snapshots 三段任何一行
2. ❌ 不动 0111d16 realtimeRunStore.ts schema(本 fork 仅消费)
3. ❌ 不实施顶栏 ▶/■(已 fork 4)/ 不实施 capture/recapture(留 fork 7)
4. ❌ 不动后端 / contracts / dsp_algo
5. ❌ 不动 ADR-12 §3 7 类 MeasurementNode 业务行为契约
6. ❌ 不实施真硬件 e2e(留 fork 8)/ 不引入新 NPM 依赖
7. ❌ 不省略 Step 1 真值核查报告(commit body 必含 EnginePanel 现状 + signalConfig type + audio devices API schema)
解锁链(本任务 zombie 后)
- ✅ Phase 3 fork 8 P_e2e.UA13-truth(议题 1 + 议题 2 端到端 e2e · 含 selectInputDevice/selectOutputDevice/selectSignal · 本 fork UI 支撑)
- ✅ ADR-13 §3.1 ④ + §3.2 ④ 用户操作流业务行为契约闭环(Phase 1 后端 + Phase 2 UI 全栈就位)
风险评估
| 风险 | 缓解 |
|---|---|
| ⚠️ /api/audio/devices/{inputs,outputs} 后端 endpoint 路径与 fork 1 实现不一致 | Step 1 grep backend_csharp/Routes/* 真值核查 + 严格对齐 schema |
| ⚠️ realtimeRunStore.signalConfig 类型与 fork 4 实施不一致 | Step 1 全文读 realtimeRunStore.ts · types/audio-device.ts SignalConfig 严格对齐 · typecheck 强制 |
| ⚠️ EnginePanel.vue 末尾追加破坏 b4a8ea2 三段 | 严守"仅末尾追加 + script setup 内段" · git diff Step 7 前 review 不动旧段 |
| ⚠️ 模块级缓存导致 dev mode HMR 状态污染 | useAudioDevices 内部用 ref · vitest mock + setActivePinia 重置 · Step 6 浏览器手测 reload 验证 |
| ⚠️ mode auto-detect computed 无限循环(watchEffect 写 store + computed 读 store) | watchEffect 仅写 store.activeMode(单向 · 推导端) · computed 不写 store · 单测 verify 不抖动 |
| ⚠️ loopback 模式 input === output 实测可能合法(虚拟 loopback device) | 红警仅文案提示 · 不强制阻止(用户拍板权 · 后端 fork 1 已 422 校验) · 文案文字"Loopback requires distinct input/output device" |
| ⚠️ design-token 用法与 b4a8ea2 不一致 | Step 1 真值核查 b4a8ea2 已落 .xy-engine-section + .xy-error 等 class · 复用不重新发明 · CSS variable 不硬编码 hex |
历史
| 时间 | 事件 | hash |
|---|---|---|
| 2026-06-02 11:50 | dispatched · 用户 11:21 双 stop fork 2+4 解锁本 fork(blocked → 可派)· 11:37 用户拍板:先 done/ 归档分类(7 子目录 ADR-AIOS-XX)+ DASHBOARD 链接批量更新(49/49 修复)· 11:50 派发 fork 5 · ClaudeA 1.0d 🧵 file · 范式参考 EnginePanel b4a8ea2(三段)+ realtimeRunStore 0111d16(7 字段)· 解锁 Phase 3 fork 8 e2e |
— |