ADR-12 #7 P0.U-measurement-electrical-recorder-stub(729327c)仅落 RecorderNode ① schema + UI stub · ②-⑤ 真业务延后。
用户 2026-05-31 14:26 拍板方向 3 三 fork 全派(#10 + #11 + 本 fork Recorder 真业务 · 总 5.5d)。
本 fork 升级 #7 stub 到真业务实施 · 严守 §4.3 Non-Goals(.wav 多轨文件持久化留下季度 · 仅内存 ring buffer + Multi-track + Marker + AB Compare)。
ClaudeA 排队 2.5d(议题⑤ 1.0d + cabin-3d 1.0d + 缓冲)+ 本 fork 1.5d = 4.0d 可控 · 与 #10 ClaudeC + #11 ClaudeD 文件正交并行。
estimated: 1.5d
preempts: []
preempted_by: null
unblocks: [ADR-12 §3.7 Recorder Node ②-⑤ 5 必填段全填 · 配合 #11 BottomDock § Task Queue 显示录音任务]
related_zombie:
- P0.U-measurement-electrical-recorder-stub (729327c · §3.7 ① schema + UI stub · 本 fork 升级 ②-⑤)
- P0.U-measurement-thd-snr (554ddd1 · ClaudeA 同 worker 设计语言标杆)
- P0.U-measurement-rms-fft-phase (8379de2 · measurement-shared 风格标杆)
P0.U-measurement-recorder-real · ADR-12 RecorderNode 真业务实施(内存 ring buffer + Multi-track + AB Compare)
Worker:ClaudeA · 前端 / 部门:前端 P0-xishell / 预计:1.5d / 优先级:P1 / 状态:ready
🔍 触发与解锁链
- 触发:ADR-12 #7(
729327c)仅落 RecorderNode ① schema + UI stub · 用户拍板方向 3 三 fork 全派 · 本 fork 升级真业务。 - 解锁条件:本 fork zombie 后,ADR-12 §3.7 Recorder Node 5 必填段全填 · 配合 #11 BottomDock § Task Queue 显示录音任务。
任务定义(基于 ADR-AIOS-12 v2.3 §3.7 + §A.14 + §A.20 + §2.11 全局红线)
将 #7 落地的 RecorderNode stub 升级为真业务实施(§3.7 ②-⑤ 5 必填段全填):
核心范围(对齐 ADR-12 §3.7 Recorder Node): 1. 5 必填段全填 ②-⑤(收敛判据 + 失败回退 + 用户操作流 + e2e 真值)· ① schema 已在 729327c 落 2. 内存 ring buffer 真业务:Multi-track(8 通道并行)+ Loop 录制 + Marker 添加 + AB Compare(2 buffer 切换播放)+ Buffer Cache(快照供 widget 复用) 3. §A.14 Recording 组件 + §A.20 Recordings 工程文件: - §A.14:Multi-track / Loop / Marker / AB Compare / Snapshot / Buffer Cache 6 项 · 本 fork 全实施(除文件持久化) - §A.20:回放 / 分析 / Overlay / Compare / Offline FFT(本 fork 实施回放 + Overlay · Offline FFT 留下季度) 4. 文件持久化(.wav 多轨)严守 Non-Goals(§4.3):仅内存 ring buffer · 浏览器关闭丢失 · 文件持久化留 ADR-AIOS-13 候选
🚨 全局红线对齐(ADR-12 §2.11 第 8+9 项):
- 响应式横竖屏:RecorderWidget 主图(波形显示)+ 控制条响应式 · 横屏=波形左+控制右 / 竖屏=波形上+控制下
- 主题色系切换:严禁硬编码 hex · 必须 var(--xs-recorder-*) design-token
完整 prompt(直接复制粘贴 worker 终端)
[U-thread]: P0.U-measurement-recorder-real
[部门]: 前端 P0-xishell
[Worker CWD]: d:/work/25_claude/workspace/AlgoDepartment/04_development/
[Occupies]: P0.K-shared-meter-dock · P5.K-meter-tap(read)
[优先级]: P1 · 1.5d
[ADR]: docs/08-implementation/40-aios/ADR/ADR-AIOS-12-xitest-realtime-arch.md(v2.3)
[业务行为契约引用]: 严格对齐 ADR §3.7 Recorder Node 5 必填段(① schema 已在 729327c · 本 fork 落 ②-⑤)+ §2.11 第 8+9 项全局红线 + §4.3 Non-Goals(.wav 文件持久化严守不实施)
[参考文档]:
- ADR-AIOS-12 §3.7 Recorder Node + §A.14 Recording 组件 + §A.20 Recordings(本 fork 主依据)
- ADR-AIOS-12 §2.11 第 8+9 项全局红线 + v2.3 前后端责任边界
- ADR-AIOS-12 §4.3 Non-Goals(.wav 多轨文件持久化留下季度 · 严守不实施)
- 已就位 stub(729327c):frontend_vue3/src/components/measurement-shared/RecorderStub.vue + types/recording-session.ts
- 已就位标杆(554ddd1):components/measurement-shared/ElectricalMeter.vue · ClaudeA 同 worker 设计语言(数值卡片 + 控制条)
- 已就位标杆(8379de2):components/measurement-shared/RMSMeter.vue · 数值显示 + ring buffer 风格
- 已就位 P5 WS(48cf0ba):MeterTapService toolKind=recorder 已支持(本 fork 直接消费 · 不动 P5)
【背景】
ADR-12 #7(`729327c`)落地 RecorderNode ① 输入输出契约 schema(stub 占位)· 本 fork 升级到 ②-⑤ 5 必填段真业务。
§A.14 Recording 组件 6 项:Multi-track(本 fork 8 通道并行)+ Loop(可循环覆盖录制)+ Marker(用户标记关键点)+ AB Compare(2 buffer 切换)+ Snapshot(转 widget Snapshot)+ Buffer Cache(供其他 widget 复用)
§A.20 Recordings 工程文件 5 项:回放 / 分析 / Overlay / Compare / Offline FFT
- ✅ 本 fork 实施:回放(WaveformChart 渲染 buffer)+ Overlay(双 buffer 叠加显示)
- ❌ 留下季度:Offline FFT(需 P7 端点)+ Compare 跨 widget(需 #11 Storage Engine 持久化)
⚠️ §4.3 Non-Goals 严守:**禁止 .wav 多轨文件持久化**(浏览器关闭后录音丢失 · 用户期望文件保存留 ADR-AIOS-13)
【执行步骤】(6 步)
Step 1 · types schema 升级(0.1d)
- 扩 types/recording-session.ts(已落):
interface RecordingBuffer {
session: RecordingSession // 已落 · ADR §3.7 ①
tracks: Float32Array[] // 8 通道 PCM 数据(ring buffer · 浏览器内存)
currentSize: number // 已录长度(samples)
maxSize: number // ring buffer 上限(默认 30s @ 48kHz × 8 = ~46MB)
}
interface RecorderState {
buffers: Map<string, RecordingBuffer> // 多 buffer · key=sessionId
activeBufferA?: string // AB Compare A 槽
activeBufferB?: string // AB Compare B 槽
playbackBuffer?: string // 当前回放 · null=实时
isRecording: boolean
isPlaying: boolean
}
- 不动 729327c 已落地 RecordingSession schema(仅扩展)
Step 2 · RecorderNode 主组件升级(0.5d · 替换 729327c stub)
- 路径:frontend_vue3/src/components/measurement-shared/Recorder.vue(替换 RecorderStub.vue)
- 主图:WaveformChart 渲染当前 buffer 8 通道波形(复用 8379de2 风格)· 实时模式滚动 / 回放模式静态
- 控制条 · 6 段(对齐 §A.14 6 项):
- [⏺ Record] / [⏸ Pause] / [⏹ Stop](ring buffer 录入)
- [⏮ Prev Marker] / [⏭ Next Marker]([🚩 Add Marker] 按当前 cursor 加 marker)
- [▶ Play] / [⏸ Pause Play] / [⏹ Stop Play](选 buffer 回放 · 走 Web Audio API)
- [💾 As Snapshot]([📤 Export to Widget] 转给 widget · 走 #10 SnapshotsPanel 接口)
- [🔄 AB] · [A] / [B] 槽切换 · Compare 模式叠加波形
- [🔁 Loop] · 循环覆盖录制(到 maxSize 自动从头覆盖)
- 数值显示:Buffer Size(MB)· Duration(s)· Markers(N)· Tracks(8)· Status(Recording/Playing/Idle)
Step 3 · ring buffer + AB Compare 核心逻辑(0.4d · 本 fork 主体)
- useRecorder.ts composable:
- subscribeMeterTap(toolKind='recorder'):订阅 P5 WS 帧 · 推 PCM 到 currentBuffer.tracks
- addMarker(label):在 currentBuffer 当前 cursor 加 marker
- switchABBuffer(slot, sessionId):切 A/B 槽 · 触发 Compare 模式
- playback(sessionId, options):用 Web Audio API AudioBufferSourceNode 回放(纯前端 · 不走后端)
- exportToWidget(targetWidgetId, sessionId):调用 #10 SnapshotsPanel 接口(若 #10 未 zombie · fallback console.warn `[need:engine-session-snapshots]`)
- ring buffer 滚动覆盖逻辑:Loop 模式下达到 maxSize 后从 index 0 重新写(不分配新内存)
Step 4 · 响应式横竖屏 + 主题色系(0.2d · 全局红线 #1#2)
- 响应式横竖屏(全局红线 #1):用 ResizeObserver 监听根容器 · aspectRatio
- 横屏(宽 ≥ 高):波形主图占 70% 宽 + 控制条侧栏 30%
- 竖屏(高 > 宽):波形主图占 70% 高 + 控制条 30% 在下方
- 主题色系(全局红线 #2):design-tokens.css 新增:
--xs-recorder-bg / --xs-recorder-track-fill / --xs-recorder-cursor / --xs-recorder-marker
--xs-recorder-status-recording / --xs-recorder-status-playing / --xs-recorder-status-idle
--xs-recorder-ab-a / --xs-recorder-ab-b / --xs-recorder-ab-compare-overlap
- 全组件 grep "#[0-9a-fA-F]\{3,8\}" 0 命中
Step 5 · 业务行为契约 5 必填段 e2e 真值(0.2d)
- 新增 e2e/recorder-real-truth.spec.ts(对齐 §3.1 RMSMeter e2e 风格 · 8379de2 标杆)
- case 1:注入 1kHz -10dBFS 1s 正弦 · 触发 Record → Stop → 断言 buffer.tracks[0] 长度 ≈ 48000 samples · 数值有效(非全 0)
- case 2:Record + Add Marker × 3 → 断言 markers 数组长度 = 3 · 时间戳递增
- case 3:回放 buffer A → Web Audio API audio context running · 实测时长 ≈ 1s
- case 4:AB Compare:buffer A(粉噪)+ buffer B(白噪)→ 双波形叠加渲染 · 颜色用 ab-compare-overlap token
- case 5:Loop 模式覆盖录制 → 达到 maxSize 后 currentSize 不再增长 · index 从 0 重置(ring buffer 验证)
- case 6:横竖屏切换布局正确(全局红线 #1)+ 主题切换浅色↔深色(全局红线 #2)
Step 6 · 自查 + commit(0.1d)
- npm run typecheck 全绿
- npm run test:unit 不退化(基线 356/3)+ 新增 6 vitest case 全过
- npm run test:e2e -- recorder-real-truth 全过
- grep `#[0-9a-fA-F]{3,8}` 在 Recorder.vue 中 0 命中(主题切换硬约束)
- grep `\.wav|writeFile|node:fs|writeFileSync` 在本 fork 改动文件 0 命中(严守 §4.3 Non-Goals · .wav 文件持久化留下季度)
- grep `audio-data|audio-buffer|fft|numpy|deconv` 在本 fork 改动文件 0 命中(沿用 ADR-07 §1.3.4 三层分工 · 数学全交 P7 · Web Audio API 仅播放不算"数学")
- 不动 729327c 已落地的 RecordingSession schema(仅扩展 RecordingBuffer + RecorderState)
- commit subject: feat(P0.U-measurement-recorder-real): upgrade RecorderNode to real business (Multi-track ring buffer + Marker + AB Compare + Web Audio playback + responsive landscape/portrait + design-token theme · ADR-12 §3.7 ②-⑤ + §A.14 6 features)
- commit trailer 三元组:[step=6/6] [pid=P0] [uid=U-measurement-recorder-real] [occupies=P0.K-shared-meter-dock+P5.K-meter-tap-read] [files=components/measurement-shared/Recorder.vue,types/recording-session.ts,composables/useRecorder.ts,e2e/recorder-real-truth.spec.ts,styles/design-tokens.css]
【验收】(stop 前必跑)
形式合规:
☐ npm run typecheck 全绿(0 error)
☐ npm run test:unit 不退化 + 新增 6 case 全过
☐ npm run test:e2e -- recorder-real-truth 6 case 全过
☐ grep `#[0-9a-fA-F]{3,8}` 在 Recorder.vue / 新增 .ts 中 0 命中
☐ grep `\.wav|writeFile|node:fs` 0 命中(严守 §4.3 Non-Goals)
☐ grep `audio-data|audio-buffer|fft|numpy|deconv` 0 命中(沿用三层分工铁律)
☐ 不动 729327c 已落地的 RecordingSession schema 既有字段(仅扩展)
☐ design-tokens.css 已加 --xs-recorder-* 9 项 token
业务行为契约(端到端真值 · 必跑):
☐ 1kHz 1s 正弦录入 → buffer 长度 ≈ 48000 · 数值有效非全 0
☐ Add Marker × 3 → markers.length = 3 · 时间戳递增
☐ 回放 buffer A · Web Audio API context running · 时长 ≈ 1s
☐ AB Compare:粉噪 + 白噪 双波形叠加 · 颜色 token 正确
☐ Loop 模式覆盖录制 · ring buffer 不增长 · index 重置
☐ 横屏 1920×1080 vs 竖屏 1080×1920 布局切换(全局红线 #1)
☐ 主题切换浅色 ↔ 深色 · Recorder 颜色变化(全局红线 #2)
【commit】
- subject + 三元组 trailer 见 Step 6
- 7 天宽限期内三元组缺失仅 warning · 6/2 起 strict mode 硬拒
【禁止】(8 项红线)
1. ❌ 禁止实施 .wav 多轨文件持久化(严守 ADR-12 §4.3 Non-Goals · 留 ADR-AIOS-13 候选)
2. ❌ 禁止在前端实装 FFT / Offline 数学计算(沿用 ADR-07 §1.3.4 三层分工 · 数学全交 P7)
3. ❌ 禁止硬编码 hex 色值(必须 var(--xs-recorder-*) design-token · 全局红线 #2)
4. ❌ 禁止跳过响应式横竖屏 e2e(必须 viewport 1920×1080 vs 1080×1920 各跑一次 · 全局红线 #1)
5. ❌ 禁止动 729327c 已落地的 types/recording-session.ts 既有字段(仅扩展 RecordingBuffer + RecorderState)
6. ❌ 禁止改 P5 后端任何接口(对齐 ADR-12 §2.11 v2.3 前后端责任边界 · 现有数据契约不变)
7. ❌ 禁止跳过 e2e 真值(必须 6 case · 含全局红线 #1#2 · 不允许仅 typecheck/test 形式合规通过就 commit)
8. ❌ 禁止越界引入 stages/ 子目录代码(本 fork 仅在 components/measurement-shared/ + composables/ + types/ + e2e/ + styles/design-tokens.css)
解锁链(本任务 zombie 后)
- ✅ ADR-12 §3.7 Recorder Node 5 必填段全填(对齐 §A.14 6 features)
- ✅ 配合 #11 BottomDock § Task Queue 显示录音任务进度(若 #11 已 zombie · 自动联动)
- ✅ 配合 #10 SnapshotsPanel:exportToWidget 把录音 buffer 转 Snapshot · 跨 widget 复用
- ✅ Workspace Preset 'Recording' 默认 widget 第 1 格就位(Waveform / Spectrogram / Markers · 本 fork 提供 Recorder 主体)
风险评估
| 风险 | 缓解 |
|---|---|
| ring buffer 30s × 8 通道 × 48kHz × 4byte ≈ 46MB 内存占用过大 | Step 3 用 SharedArrayBuffer + Float32Array views(不复制 · 仅引用)· 浏览器单 widget < 100MB 可接受 · 用户可调 maxSize 默认 30s |
| Web Audio API AudioBufferSourceNode 跨浏览器兼容(Edge/Chrome/Firefox) | Step 3 用 vueuse @vueuse/sound 兼容封装 · IE 不支持(MDN 规范无要求 IE)· Electron 26+ 100% 支持 |
| AB Compare 双波形渲染性能(8 通道 × 48000 samples × 2 buffer) | Step 2 WaveformChart 用 Canvas 降采样(峰值法 · 每像素 1 数据点)· 60fps 可达 |
| Marker 时间戳与 ring buffer 滚动覆盖冲突(Loop 模式下旧 marker 失效) | Step 3 useRecorder.addMarker 在 Loop 覆盖时自动清理过期 marker · 保留最近 N 条 |
| #10 SnapshotsPanel 未 zombie 时 exportToWidget 调用失败 | Step 3 useRecorder fallback console.warn [need:engine-session-snapshots] · 不阻塞本 fork zombie · 用户在 #10 zombie 后切真接口 |
历史
| 时间 | 事件 | hash |
|---|---|---|
| 2026-05-31 14:30 | dispatched · 用户拍板方向 3 三 fork 全派(#10 + #11 + 本 fork)· ClaudeA 排队 4.0d 末尾 | — |