⚠️ 本 fork 已 aborted(2026-06-07 16:25 · 用户拍板组合 1 = A1+B1) · 原因:架构边界错位(详见 frontmatter
aborted_reason) · 替代:R2 §3.5.1 重新设计的新 fork(消费现有数据源 + 前端可视化 · 不造 P7 算法层) · ClaudeB 不需要执行本 prompt · 等 R2 accepted 后看 DASHBOARD §推荐新派 (注:此处不修改正文 · 保留下方 line 27+ 原内容用于历史归档与教训沉淀)
(以下为历史归档原文 · aborted 不再生效 · 仅作为 R2 修订前的设计快照保留)
P_dsp.A17R1.F1 · Phase Meter 算法实装(P7 端点 + P5 路由 + WS frame phase_meter_v1)
worker:ClaudeB(DSP+sidecar 兼任) 部门:DSP+P7 sidecar(+ P5 路由 + P_contracts protocol) 预计:2.0d(关键路径起点 ⅙ 段) 优先级:P0 状态:ready
🔍 触发与解锁链
- 触发:ADR-17-R1 accepted(2026-06-07 11:03 · 用户拍板)解锁 + 用户 11:03 拍板
start P_dsp.A17R1.F1-phase-meter-algorithm - 前置:ClaudeB ADR-18 F6+F7 + ADR-15 F6 🚀task 三 task 跑完即接本 fork(ADR-15 F6 独立 worktree 不阻塞)
- 解锁:F2 transfer-function-algorithm(同 ClaudeB · 复用本 fork phase 算法)+ F4 spectrogram(复用 STFT pipeline)+ F6 e2e ≥ 12 case 真值断言 🏆
任务定义(基于 ADR-17-R1 §3.5.1 完整 5 段 + 引用 ADR-12 §3.4)
实现 Phase Meter 算法层完整闭环:NEW P7 sidecar 端点 /analyze/phase_meter(numpy/scipy 计算 phaseWrapped + phaseUnwrapped + groupDelay = -dφ/dω)+ P5 MeterTapService 路由扩展 'phase-meter' type + WS frame schema 加 phase_meter_v1 + 单元测试覆盖业务契约 §3.5.1 ② 3 收敛判据 + ③ 5 失败回退 · 严守三层分工铁律(L1 P5 路由零数学 / L2 P7 sidecar 全部 numpy 计算 / L3 前端零数学 · 本 fork 不做 PhaseMeterPanel.vue · 那是 F3 之后的下一期或 F6 e2e 自适配现有 PhaseChart stub)。
完整 prompt(直接复制粘贴 worker 终端)
[U-thread] P_dsp.A17R1.F1-phase-meter-algorithm
[部门] DSP+P7 sidecar (P7-pysidecar + P5-backend-csharp + P_contracts)
[Worker CWD] d:/work/25_claude/workspace/AlgoDepartment/04_development/
[Occupies] P5.K-services + P_contracts.K-protocol + P7.K-pysidecar + P0.K-shared-meter-dock
[优先级] P0 · 关键路径起点 1/6 段(F1→F2→F4→F5→F6 + F3 并行)
[ADR] docs/08-implementation/40-aios/ADR/ADR-AIOS-17-R1-realtime-widgets-algorithm-impl.md
[参考文档]
- 业务契约:ADR-17-R1 §3.5.1 完整 5 段(① MeterFrame_Phase schema · ② 3 收敛判据 · ③ 5 失败回退 · ④ 5 步操作流 · ⑤ 2 个 e2e 真值)
- 业务契约 ① 引用 ADR-12 §3.4 line 750-768(MeterFrame_Phase 字段定义:freqs / phaseWrapped / phaseUnwrapped / groupDelay / averagedCount / resolution · 单位:phase=° · groupDelay=ms)
- fork 描述:ADR-17-R1 §4 fork 表 F1 行
- F5 标本(同 ClaudeB · 同部门后端+sidecar):prompts/done/ADR-AIOS-17/F5-log-module-and-display-module-infra--161fbf9.md(LogModuleService NEW + DSP 落盘 + protocol-v1 扩展 · 14 case TDD)· **沿用其 frame 注册 + WS 路由 + 单测风格**
- F1 标本(后端 IO API):prompts/done/ADR-AIOS-17/F1-io-connector-device-api--5f9609e.md(MeterTapService 扩展 + 4 Adapter + 12 case TDD)
- 三层分工铁律:ADR-07 §1.3.4(L1 .NET 端零数学 · L2 P7 sidecar 全部 numpy/scipy · L3 前端零数学 · 本 fork **DSP 数学全在 P7 端点**)
- contract-v1.0 frozen:不可破坏老 frame type · phase_meter_v1 走 protocol-v1 扩展(向后兼容)
- 教训承接:ADR-13 H1/H2/H3 三轮 hotfix 触底 = "5 widget stub 占位" · 本 R1 业务契约 5 必填段硬阻塞 · 拒绝任何 stub 占位 · phase 算法必须实装真数学
【背景】
ADR-17 7 fork 全 zombie 但 §3.4 line 517-519 假设 ADR-12 时代 7 widget 算法已 fulfilled · 实际 ADR-13 H1/H2/H3 触底证明仅 RMS+FFT 2/7 实装 · 5 widget(phase/transfer/scope/spectrogram/electrical)仍 stub 占位。
用户 2026-06-04 12:37 + 2026-06-06 21:09 双次反馈"phase transfer 等都不显示 · 测量插件只有 fft+rms 可用"症状原话完全相同。
ADR-17-R1 v0.1 accepted · 6 fork 12d 跨栈实装 5 widget 算法 + F6 e2e ≥ 12 case 真值断言闭环。
本 fork 是关键路径起点 · 实装 Phase Meter 算法的真数学(L2 P7 sidecar)+ L1 P5 路由 + WS frame 协议 · 业务契约 §3.5.1 ② 3 收敛判据 + ③ 5 失败回退全覆盖。
【架构关键约束】
- ⚡ **三层分工铁律**:L1 P5 (.NET) 零数学(只做 MeterTapService 路由扩展 + WS frame 派发)· L2 P7 (sidecar) 全部 numpy/scipy(phaseWrapped + phaseUnwrapped + groupDelay)· L3 前端 (Vue) 零数学(本 fork 不动 · F6 e2e 自适配现有 PhaseChart stub 或 widget endpoint selector)
- 🎨 **F1 范围**:NEW P7 端点 `/analyze/phase_meter` + P5 路由扩展 'phase-meter' type + WS frame `phase_meter_v1` + 单测 ≥ 8 case · **不做前端 PhaseMeterPanel.vue**(那是 R1 之后的下一期 · 本 fork 后端就位即可)· 不做 widget endpoint selector(ADR-17 F6 已 zombie · 复用现有 4 类 selector)
- 📋 **F2 transfer 复用基础**:本 fork phaseWrapped + phaseUnwrapped + groupDelay 的 numpy 实现是 F2 transfer function 的基础(transfer 在双通道场景下复用 cross-spectral density · 本 fork 是单通道 phase response)· 设计 P7 端点时考虑可扩展(ref+measure 双通道在 F2 加)
- 🚨 **拒绝 stub 占位**(ADR-13 触底教训):phase 算法必须用 numpy.angle + numpy.unwrap + numpy.gradient 真数学计算 · 不允许返回 zeros 数组 / 随机数 · F6 e2e 真值断言会验数据准确性
【执行步骤】
Step 0 · 现有代码核查(强制门槛 · F2 教训承接)
- grep "MeterTapService" 在 backend_csharp/Services/ 中找现有路由扩展
- grep "/analyze/" 在 pysidecar/ 中找现有端点(ADR-12 时代已有 19 端点 · 加 5 端点 R1 共 24 个)
- grep "phase" 在 protocol/protocol-v1.json 中确认 frame type 命名空间不冲突
- read F5 标本 docs/08-implementation/40-aios/prompts/done/ADR-AIOS-17/F5-log-module-and-display-module-infra--161fbf9.md(LogModuleService 注册 + plugin 体系 + frame 协议 + DI 注册 · 沿用风格)
- 输出 internal todo:确认 P7 端点目录(estimated `pysidecar/analyze/` 或 `pysidecar/routes/`)+ P5 MeterTapService 路由扩展点 + protocol-v1 扩展位置
Step 1 · NEW P7 端点 `/analyze/phase_meter`(L2 数学层 · 净新增)
- pysidecar/analyze/phase_meter.py(或现有 routes 目录约定路径 · 看 Step 0 真值)
- 输入:request body { samples: float[] | base64 PCM, sampleRate: int, fftSize: int (1024|4096|8192), window: 'hanning'|'hamming'|'blackman'|'flat-top'|'rect', overlap: 0|0.5|0.75|0.875, mode: 'wrapped'|'unwrapped'|'groupDelay' }
- 输出 JSON · 字段对齐 ADR-12 §3.4 ① MeterFrame_Phase:
- freqs: float[] (bin 频率 Hz · 长度 fftSize/2+1)
- phaseWrapped: float[] (degree · ±180)
- phaseUnwrapped: float[] (degree · 用户 mode='unwrapped' 时填)
- groupDelay: float[] (ms · -dφ/dω 计算 · 用户 mode='groupDelay' 时填)
- averagedCount: int
- resolution: float (Hz/bin = sampleRate/fftSize)
- 算法实装:
- 加窗 + 重叠加平均 STFT → numpy.fft.rfft → numpy.angle 得 phaseWrapped(转 degree:np.degrees)
- phaseUnwrapped = numpy.unwrap(phase, period=2*np.pi) 然后 np.degrees
- groupDelay:numpy.gradient(phaseUnwrapped_rad, freq_rad) * (-1) · 转 ms(× 1000 / (2π))
- 信号过弱判定(§3.5.1 ③ row3):if mean(magsDb) < noiseFloorDb + 6dB → return 422 + 错误消息
- 计算超时保护(§3.5.1 ③ row5):asyncio.wait_for · 超 200ms 自动降 fftSize(8192→4096)+ 返回 warning
- 单测 pysidecar/tests/test_phase_meter.py(≥ 4 case · pytest)
- case1: 注入 1kHz sine -10dBFS · 1kHz bin phase 在 0±5°(假定 ref 同步 · 实际由 P5 端时间戳)
- case2: phaseUnwrapped 跨 360° 跳变 · 算法不返回 NaN/Inf
- case3: groupDelay 全段无 NaN/Inf(§3.5.1 ② row2)
- case4: 信号过弱 · noiseFloorDb + 3dB 信号 → 返 422
Step 2 · P5 MeterTapService 路由扩展(L1 路由层 · 不做数学)
- backend_csharp/Services/MeterTapService.cs(或扩展点 · 看 Step 0 真值)
- 加 toolKind='phase-meter' 路由分支 → 调 P7 端点 /analyze/phase_meter
- 复用现有 PysidecarMeterBridge 异步调用模式(F1 / F5 标本)
- WS subscription topic:`phase-meter:<chainId>:<channelId>` · 默认 25fps(同 §3.3 log_module 模式)
- 限流:同 30fps/tool/client(ADR-12 §2.10 三层分工已锁)
- DI 注册到 Program.cs(若需)
Step 3 · WS frame schema 扩展(P_contracts protocol-v1)
- protocol/protocol-v1.json(P_contracts.K-protocol)
- 加 frame type 'phase_meter_v1' schema:
```json
{
"v": 1,
"type": "phase_meter_v1",
"chainId": "string",
"channelId": "number",
"timestamp": "number",
"sampleRate": "number",
"fftSize": "number",
"window": "enum",
"overlap": "enum",
"freqs": "Float32Array",
"phaseWrapped": "Float32Array",
"phaseUnwrapped": "Float32Array (optional)",
"groupDelay": "Float32Array (optional)",
"averagedCount": "number",
"resolution": "number"
}
```
- 不破 contract-v1.0 frozen(扩展 frame type 是向后兼容动作)
Step 4 · NEW Models(若 P5 需 record · 不强制 · 看现有 Models 结构)
- 若现有 MeterFrame.cs 已有 discriminated union 模式 · 加 PhaseMeterFrame 子 record · 字段对齐 ADR-12 §3.4 ①
- 若用 Json polymorphism · 加 [JsonDerivedType] 注册
Step 5 · 单元测试(xunit · TDD · ≥ 8 case 总计 P5+pysidecar)
- Tests/Services/MeterTapServicePhaseMeterTests.cs(P5 路由 · ≥ 4 case)
- case1: toolKind='phase-meter' · WS subscription topic 正确生成
- case2: P7 调用失败(模拟 502)→ §3.5.1 ③ row2 P7 崩溃回退 · 顶栏红警 + 自动重启 trigger
- case3: WS 帧序列化往返 · phase_meter_v1 schema 字段全
- case4: 限流 30fps/tool/client(同 ADR-12 已锁)
- pysidecar/tests/test_phase_meter.py(已在 Step 1 写 4 case)
Step 6 · 验收 + commit
- dotnet build ✓ 0 错误 0 警告
- dotnet test ✓ 至少 +4 case 全过
- pytest pysidecar/tests ✓ 至少 +4 case 全过
- 不破坏 contract-v1.0 frozen(老 frame 全保留 · phase_meter_v1 是 additive)
- F1 / F3 / F5 / F6(已 zombie 的 ADR-17 fork)路由配置不修改
- commit message 见下方 trailer
【验收】
- ☐ 6-7 文件改/新建:pysidecar/analyze/phase_meter.py(净新增)+ backend_csharp/Services/MeterTapService.cs(扩展 toolKind 分支)+ protocol/protocol-v1.json(扩展 frame type)+ Models/MeterFrame/PhaseMeterFrame.cs(若 P5 用 record · 可选)+ Program.cs(DI · 可选)+ Tests/Services/MeterTapServicePhaseMeterTests.cs + pysidecar/tests/test_phase_meter.py
- ☐ 测试新增 ≥ 8 case 总计(P5 ≥ 4 + pysidecar ≥ 4)
- ☐ dotnet build 0 错 0 警 · dotnet test + pytest 全过
- ☐ ADR-17-R1 §3.5.1 ② 3 收敛判据全过(phaseWrapped std<5° 跨 8 帧 / groupDelay 无 NaN/Inf / FPS≥25)
- ☐ §3.5.1 ③ 5 失败回退覆盖(WS 断 / P7 崩溃 / 信号过弱 / 相位环绕异常 / 计算超时降 fftSize)
- ☐ 三层分工铁律严守(L1 .NET 零数学 · L2 P7 全 numpy · L3 前端不改)
- ☐ 拒绝 stub 占位 · phase 算法实装真数学(numpy.angle + unwrap + gradient)
- ☐ commit message 含三元组 trailer
【commit】
subject: feat(P_dsp.A17R1.F1/phase-meter-algorithm): NEW P7 端点 /analyze/phase_meter (numpy phaseWrapped+unwrapped+groupDelay) + P5 MeterTapService phase-meter 路由 + WS frame phase_meter_v1 + 8 case TDD
trailer:
[step=6/6] [pid=P_dsp+P5+P_contracts] [uid=P_dsp.A17R1.F1-phase-meter-algorithm] [occupies=P5.K-services+P_contracts.K-protocol+P7.K-pysidecar+P0.K-shared-meter-dock]
[adr=ADR-AIOS-17-R1 §3.5.1] [files=pysidecar/analyze/phase_meter.py,backend_csharp/Services/MeterTapService.cs,protocol/protocol-v1.json,Models/MeterFrame/PhaseMeterFrame.cs,Program.cs,Tests/Services/MeterTapServicePhaseMeterTests.cs,pysidecar/tests/test_phase_meter.py]
[ipc=rest+ws-stream]
【禁止】
- ❌ 禁止破坏 contract-v1.0 frozen(老 frame type 0 改 · phase_meter_v1 是 additive 扩展)
- ❌ 禁止在 .NET 端做任何 DSP 数学(三层分工 · phase/unwrap/groupDelay 全在 P7 端点 · L1 P5 仅做 MeterTapService 路由 + WS frame 派发)
- ❌ 禁止超前实施前端 PhaseMeterPanel.vue(本 fork 不做 · F6 e2e 自适配现有 PhaseChart stub 或 widget endpoint selector)
- ❌ 禁止超前实施 F2 transfer function(那是下一 fork 范围 · 本 fork 仅 phase 单通道)
- ❌ 禁止动 ADR-17 已 zombie 的 5 fork 路由配置(F1/F3/F4/F5/F6 · 本 fork 仅扩 toolKind='phase-meter' 分支)
- ❌ 禁止返回 stub 占位数据(zeros / 随机数 · ADR-13 触底教训)· phase 算法必须用 numpy.angle + unwrap + gradient 真计算
- ❌ 禁止跳过单元测试(≥ 8 case · 验收硬门槛 · F6 e2e 真值断言会验数据准确性)
- ❌ 禁止 commit 缺三元组 trailer
解锁链(本任务 zombie 后)
- ✅ R1 F2
P_dsp.A17R1.F2-transfer-function-algorithm(同 ClaudeB · 2.5d Smaart 核心 · 复用本 fork phase 算法 + cross-spectral density 双通道扩展) - ✅ R1 F4
P_dsp.A17R1.F4-spectrogram-algorithm-and-ui(ClaudeB+A 2.0d · 复用 STFT pipeline · ring buffer + WebGL Canvas) - ✅ R1 F6
P_e2e.A17R1.F6-truth-e2e-5-widgets-algorithm🏆(等 F1+F2+F3+F4+F5 全 zombie · ClaudeC 2.0d · ≥ 12 case 真值断言 · 解锁 ADR-17-R1 fulfilled · 彻底解决用户原话症状)
风险评估
| 风险 | 缓解 |
|---|---|
| numpy/scipy phase unwrap 算法易出错(±180° 跳变 / 360° 累积漂移) | numpy.unwrap 用工业标准 period=2*np.pi · case2 验跳变无 NaN/Inf · 参考 librosa.phase_vocoder 风格 |
| groupDelay 计算频率分辨率与 numpy.gradient 边界处理 | gradient 默认中央差分 · 边界自动用前向/后向 · case3 验全段无 NaN/Inf · 后续 F2 transfer 同算法复用 |
| P7 sidecar 计算超时(fftSize=8192 + overlap=0.875 · 实测 > 200ms) | Step 1 实装自动降级机制(8192→4096)· asyncio.wait_for 超时保护 · case 单测 + 后续 F4 spectrogram 同模式复用 |
| WS frame phase_meter_v1 与现有 meter_v1 / log_module_v1 命名冲突 | 走独立 type 字段(JSON discriminator)· Step 3 schema 明确 · case4 单测验路由 |
| F2 transfer 复用 phase 算法时 API 设计兼容性(单通道 vs 双通道) | Step 1 P7 端点设计为单通道 + 预留 ref+measure 字段 · F2 加双通道 case · 不破坏本 fork 单通道 case |
| ADR-13 H1/H2/H3 触底复发(stub 占位) | F6 e2e 真值断言 ≥ 12 case 硬验证(phase 1kHz std<5° + groupDelay 1kHz PEQ +6dB 显著变化)· 不允许 stub 走 e2e |
历史
| 时间 | 事件 | hash |
|---|---|---|
| 2026-06-07 11:03 | dispatched(ADR-17-R1 accepted 解锁 + 用户 11:03 拍板 start F1+F3 · 关键路径起点) | (待 commit) |