P5.U-runtime-mode-refactor · RuntimeTarget 枚举重构(ADR-AIOS-08 §2.4 fork)
目标 worker:ClaudeB(主仓 04_development/ · P5-backend-csharp 主战场)
[涉及部门]:后端 (backend-csharp) · 推荐 skill:dotnet-realtime-communication
预计:2.0d · 优先级:P1 · 状态:⚪ ready(2026-05-30 09:11 派发 · ADR-AIOS-08 accepted v1.1)
🔍 触发与解锁链
| 触发 | hash | 状态 | 影响 |
|---|---|---|---|
| ADR-AIOS-08 accepted v1.1 | 2026-05-29 17:25 | ✅ 用户拍板 | 10 fork U-thread 启动 · 后端三线先派 |
| 用户拍板方向 A 就地扩展 | 2026-05-30 09:00 | ✅ 用户原话 "zhujian" | ❗ 干净重构(本 fork 现状无冲突 · 二态 → 枚举) |
| RuntimeModeService.cs 64 行 | 已实装 | ✅ 永久 | 二态 normal/legacy · 已注入 WsBroadcastService · 已广播 mode_changed |
| AWE Designer 对标 | ADR §2.4 用户拍板 | ✅ 永久 | 7 RuntimeTarget(pc-native-0~3 + dsp-21489/21569/hexagon-v73) |
→ 跨栈独立(后端 .NET) · 不修改 contract-v1.0(已 frozen) · 走 dev-api /api/runtime/* + WS runtime:switched(待 K2-protocol-v2 §runtime-selector 正式入)。
完整 prompt(直接复制粘贴 worker 终端)
[U-thread] P5.U-runtime-mode-refactor
[部门] 后端 .NET · ADR-AIOS-08 §2.4 引擎运行核心选择(RuntimeModeService 64 行 → RuntimeTargetService 枚举集合 · 对标 AWE Designer)· skill: dotnet-realtime-communication
[Worker CWD] d:/work/25_claude/workspace/AlgoDepartment/04_development/
[Occupies] P5.K-services-runtime(write · 重构 RuntimeModeService · 新增 RuntimeTargetService) + P5.K-api-routes(write · 新增 /api/runtime/{available,switch}) + P5.K-controllers(write · 新增 RuntimeController) + P5.K-ws-broadcast(write · 新增 runtime:switched event · 兼容旧 mode_changed)
[优先级] P1 · 2.0d · 跨栈独立 · 与 P5.U-error-channel(0.5d) / P5.U-perf-service(2.0d) 同 ClaudeB 并行(三 fork 文件正交)
[ADR] d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/ADR/ADR-AIOS-08-xilink-stage-ux.md (§2.4 + §3.2 边界铁律 #3 切换不破坏链路状态 + §3.2 负面后果 LEGACY_SOURCE_INJECT_MODE_MAP + §4.2 fork P5.U-runtime-mode-refactor)
[参考文档]
- 上述 ADR 主文(accepted v1.1)· 重点读 §2.4 RuntimeTarget 完整定义(pc-native + dsp-simulated + 7 预定义)+ §3.2 边界铁律 #3(切换不破坏链路状态)+ §3.2 LEGACY_SOURCE_INJECT_MODE_MAP(7 天宽限期)+ §4.2 fork 实施清单
- 同部门标本(强制 read · 4 维度对齐):d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/prompts/done/P5.U-autotune-batch-pipeline--1898c6f.md(同部门 ClaudeB · 同 P5 · REST + DI + 单测模式)+ P5.U-meter-tap-multi-tool--48cf0ba.md(WS 广播模式)
- **🚨 现状必读(本 fork 干净重构 · 无冲突 · 但需保留向后兼容)**:
· backend_csharp/Services/AudioEngine/RuntimeModeService.cs(64 行 · 全文必读):
- 第 8 行 `_currentMode = "normal"`(二态)
- 第 18-25 行 `TrySetMode()`(只接受 normal/legacy)
- 第 35-38 行 `ResolveAudioEngineMode()`(派生 embedded/pc_sim)
- 第 43-44 行 `HandleGetMode()`(WS handler · ack: get_mode_ack)
- 第 49-62 行 `HandleSetMode()`(WS handler · 广播 mode_changed)
· backend_csharp/Services/AudioEngine/AudioEngineService.cs(看 ResolveAudioEngineMode 调用点 · 切换时如何重新 init)
· backend_csharp/Services/AudioEngine/AudioEngineChainBuilder.cs(看链路重建流程)
· backend_csharp/Services/AudioEngine/DeviceSwitchRestartCoordinator.cs(看现有"挂起 → 重启"协调器 · 本任务 SwitchRuntimeAsync 复用此模式)
· backend_csharp/Services/Source/(5 文件 · grep injectMode/pcInject/dspGen 看 source 模块是否真有"PC 注入 vs DSP 直接生成"控制变量 · ADR §1.3 说 0 命中 · 需二次核实 · 若有需走 LEGACY_SOURCE_INJECT_MODE_MAP)
· backend_csharp/Services/Source/SourceSignalService.cs / SourceRuntimeStateService.cs / SourceAudioProgramBridge.cs(优先核查这 3 个)
· backend_csharp/Routes/(看现有 REST 路由风格 · LegacyTestRoutes.cs / MeterDevApiRoutes.cs)
- 上游 ADR:d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/ADR/ADR-AIOS-05-xistudio-workspace.md(§LEGACY_*_MAP 三件套模式 · 必读)
- contract-v1.0(已 frozen · 不修改):d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/contracts/protocol-v1.md(本任务走 dev-api · 待 K2-protocol-v2 §runtime-selector 正式入)
- 同期派发 P5.U-error-channel + P5.U-perf-service(读以同步 namespace · 避免冲突):active/P5.U-error-channel.md + active/P5.U-perf-service.md
【背景】
ADR-AIOS-08 已 accepted v1.1(2026-05-29 17:25)· 用户 2026-05-30 09:00 拍板「方向 A 就地扩展」原话"zhujian"。
🚨 **真值核查发现**:
- RuntimeModeService.cs **路径在 `Services/AudioEngine/`**(不是 ADR §1.3 第二份核查写的 `Services/`)· 64 行二态(normal/legacy)+ 派生(embedded/pc_sim)
- 已注入 WsBroadcastService + 已广播 `mode_changed` · 已实装 WS handler `HandleGetMode/HandleSetMode`
- ⚠️ ADR §1.3 说 source "PC 注入 vs DSP 直接生成" grep 0 命中 · 但需二次核实 `Services/Source/` 5 个文件(若实际有控制变量 · 走 LEGACY_SOURCE_INJECT_MODE_MAP)
**本任务核心**:**重构二态 → 枚举 + 向后兼容**。
- ✅ 新增 `RuntimeTargetService` 类(7 RuntimeTarget 枚举):pc-native-0/1/2/3 + dsp-21489/21569/hexagon-v73
- ✅ 保留 `RuntimeModeService` 64 行旧 API(标 [Obsolete] · 7 天宽限期)· 内部委托给 RuntimeTargetService
- ✅ LEGACY_RUNTIME_MODE_MAP:`{ "normal": "pc-native-0", "legacy": "pc-native-legacy" }` 兼容旧 .xilink 持久化
- ✅ 新增 REST `GET /api/runtime/available` + `POST /api/runtime/switch`
- ✅ 新增 WS event `runtime:switched`(兼容旧 `mode_changed` · 同时广播双消息 · 7 天宽限期后删旧)
- ✅ `SwitchRuntimeAsync(targetId)` 复用 DeviceSwitchRestartCoordinator 模式(挂起链路 → 重新初始化 module → 恢复链路)
- ❌ source 模块 "PC 注入 vs DSP 直接生成" 控制变量删除(若存在 · 走 LEGACY_MAP)
**ADR §2.4 RuntimeTarget 完整 schema**(对标 AWE Designer):
```csharp
public record RuntimeTarget(
string Id, // "pc-native-0" / "dsp-21489" / etc
string Kind, // "pc-native" / "dsp-simulated"
string Label, // UI 显示
int? ThreadAffinity, // PC native 专属(0/1/2/3)
DspProfile? DspProfile, // DSP simulated 专属
bool Available,
string? UnavailableReason
);
public record DspProfile(
string ChipModel, // "ADSP-21489" / "ADSP-21569" / "Hexagon-V73"
int MaxMips,
int MaxMemoryKB,
int SampleRateHz
);
预定义 7 RuntimeTarget(MVP): - pc-native-0~3:主 + 辅 ½/3 线程 · ThreadAffinity 0~3 - dsp-21489:ADSP-21489 · 450 MIPS · 5 MB SRAM · 48kHz - dsp-21569:ADSP-21569 · 800 MIPS · 16 MB SRAM · 96kHz - hexagon-v73:Qualcomm Hexagon V73 · 2000 MIPS · 32 MB · 192kHz
契约策略:不修改 contract-v1.0(已 frozen) · /api/runtime/* 走 dev-api · runtime:switched WS 事件向后兼容(待 K2-protocol-v2 §runtime-selector 正式入)。
【执行步骤】 1. 自查 + 读现状(必读 · 二次核实 source 控制变量): git status # 工作区干净 git branch --show-current # = xistudio git pull origin xistudio --no-rebase # 拿同期派 P5.U-error-channel/perf-service(若已 push) # 必读现状: cat backend_csharp/Services/AudioEngine/RuntimeModeService.cs # 64 行全文 cat backend_csharp/Services/AudioEngine/AudioEngineService.cs # 看 ResolveAudioEngineMode 调用点 cat backend_csharp/Services/AudioEngine/DeviceSwitchRestartCoordinator.cs # 看现有"挂起→重启"协调 # 二次核实 source 控制变量(ADR §1.3 说 0 命中 · 但需自己 grep): grep -rn -i "injectMode|pcInject|dspGen|inject_mode|generate_mode" backend_csharp/Services/Source/ # 若有命中 · 落 LEGACY_SOURCE_INJECT_MODE_MAP(本任务范围内)· 若 0 命中 · ADR §1.3 准确
- 新增 backend_csharp/Models/Runtime/(目录新建):
- RuntimeKind.cs(string-backed enum:"pc-native" / "dsp-simulated")
- RuntimeTarget.cs(record · 上述 schema)
- DspProfile.cs(record · 上述 schema)
- EngineRuntimeState.cs(record · current/available/switching · 对齐 ADR §2.4 前端 types/runtime.ts)
- RuntimeAvailableResponse.cs(record · GET /api/runtime/available 响应)
- RuntimeSwitchRequest.cs(record · POST /api/runtime/switch 请求 · { targetId, force?: bool })
-
RuntimeSwitchResponse.cs(record · POST 响应 · { success, current, durationMs, error? })
-
新增 backend_csharp/Services/Runtime/RuntimeTargetService.cs(新核心服务):
- public interface IRuntimeTargetService {
List
GetAvailable(); RuntimeTarget GetCurrent(); Task SwitchRuntimeAsync(string targetId, CancellationToken ct); string MapLegacyMode(string legacy); // "normal" → "pc-native-0" · "legacy" → "pc-native-legacy" string MapToLegacyMode(string targetId); // 反向 · 兼容 RuntimeModeService 旧 API } -
内部: · 7 RuntimeTarget 预定义列表(static readonly · 启动时构建) · _currentTargetId 私有状态(默认 "pc-native-0") · LEGACY_RUNTIME_MODE_MAP 字典(冰封 · readonly Dictionary) · SwitchRuntimeAsync 调用流程: 1) 校验 targetId 有效性 · 无效返回 400 2) 若 _currentTargetId == targetId · 短路返回 success(no-op) 3) 调 DeviceSwitchRestartCoordinator(或仿其模式)挂起 AudioEngine 4) 重新初始化 module(调 AudioEngineChainBuilder) 5) 恢复 AudioEngine 启动 6) WS 广播 runtime:switched(新)+ mode_changed(旧 · 兼容)双消息 7) 返回 RuntimeSwitchResponse{ success=true, current=newTarget, durationMs } · 失败回滚:任一步骤异常 → 回滚到原 _currentTargetId · 返回 success=false + error
-
重构 backend_csharp/Services/AudioEngine/RuntimeModeService.cs(向后兼容包装):
- 加
[Obsolete("Use IRuntimeTargetService instead. This will be removed after 2026-06-06 (7-day grace).")]类级别 - 构造函数加 IRuntimeTargetService 注入
- TrySetMode(string mode) → 内部调 RuntimeTargetService.MapLegacyMode(mode) + SwitchRuntimeAsync(targetId).Result(同步包装 · 不破坏旧调用方)
- CurrentMode → RuntimeTargetService.MapToLegacyMode(GetCurrent().Id)
- ResolveAudioEngineMode → 保留旧逻辑 · 但用 RuntimeTargetService.GetCurrent().Kind 做判定
- HandleGetMode/HandleSetMode → 保留旧 WS API · 内部委托新服务
-
❌ 零破坏:所有旧调用方(AudioEngineService 等)零修改
-
新增 backend_csharp/Controllers/Runtime/RuntimeController.cs:
- Route("api/runtime")
- GET /api/runtime/available:返回 List
(7 预定义 + Available 状态)· 不需 query - POST /api/runtime/switch:body RuntimeSwitchRequest · 调 IRuntimeTargetService.SwitchRuntimeAsync · 返回 RuntimeSwitchResponse
- 错误处理:targetId 无效 400 · 切换中 409(Conflict · 已有 switching)· 切换失败 500 + 详细 error message · 超时 504(默认 30s)
-
不修改既有 REST(零回归)
-
WS handler 注册(可能在 Program.cs 或 WebSocket/ 目录):
- 新增 handler "runtime:get_available" → JSON List
- 新增 handler "runtime:switch" → 调 SwitchRuntimeAsync · 广播 runtime:switched
-
保留旧 handler "get_mode" + "set_mode"(委托新服务 · 标 deprecated)
-
DI 注册(Program.cs):
- AddSingleton
() - 修改 RuntimeModeService 注册 · 加 IRuntimeTargetService 注入
-
验证 WsBroadcastService / DeviceSwitchRestartCoordinator / AudioEngineChainBuilder 已注册
-
(可选 · source 控制变量迁移)若 step 1 grep 命中 source 控制变量:
- 新增 LEGACY_SOURCE_INJECT_MODE_MAP 在 RuntimeTargetService 内
- SourceSignalService 等 grep 命中点 · 删 inject_mode 控制变量 · 改用 RuntimeTargetService.GetCurrent().Kind 判断
-
若 .xilink 文件含 inject_mode 字段 · 加载时自动迁移到 RuntimeTarget(7 天宽限期)
-
单测 + 验收: cd backend_csharp # 新增 Tests/Services/Runtime/RuntimeTargetServiceTests.cs(>= 6 case:GetAvailable 返回 7 + GetCurrent 默认 + Switch 成功 + Switch 无效 ID + Switch 同 ID 短路 + MapLegacyMode 双向) # 新增 Tests/Services/AudioEngine/RuntimeModeServiceTests.cs(>= 3 case:TrySetMode normal → pc-native-0 + TrySetMode legacy → pc-native-legacy + CurrentMode 反向映射) # 新增 Tests/Controllers/Runtime/RuntimeControllerTests.cs(>= 3 case:GET available 200 + POST switch 成功 + POST 无效 targetId 400) # 至少 12 个新 case dotnet build # 零错误零警告 dotnet test # 通过 + 新增 >= 12 用例(基线 147/0 → >= 159/0) # 手动 e2e(可选): curl http://localhost:5000/api/runtime/available curl -X POST http://localhost:5000/api/runtime/switch -H "Content-Type: application/json" -d "{\"targetId\":\"pc-native-1\"}"
-
Commit + push: git add backend_csharp/Models/Runtime/ \ backend_csharp/Services/Runtime/ \ backend_csharp/Services/AudioEngine/RuntimeModeService.cs \ backend_csharp/Controllers/Runtime/ \ backend_csharp/Tests/Services/Runtime/ \ backend_csharp/Tests/Services/AudioEngine/ \ backend_csharp/Tests/Controllers/Runtime/ \ backend_csharp/Program.cs git commit -m "..."(见下) git push origin xistudio
【验收】 - 11-13 文件落地(Models/Runtime ×7 + Services/Runtime ×1 + Services/AudioEngine/RuntimeModeService ×1 改动 + Controllers/Runtime ×1 + Tests ×3 + Program.cs DI) - dotnet build 零错误零警告 · dotnet test 全绿(基线 147/0 → >= 159/0 · +12 用例) - ✅ 现状未破坏:RuntimeModeService 旧 API 保留(标 [Obsolete] · 内部委托) · TrySetMode("normal") / CurrentMode / ResolveAudioEngineMode / HandleGetMode / HandleSetMode 五入口 100% 兼容 · 旧客户端零回归 - ✅ 新功能可达:GET /api/runtime/available 返回 7 RuntimeTarget · POST /api/runtime/switch 切换并广播 runtime:switched + mode_changed 双消息 · Swagger UI 可见 - ✅ LEGACY_RUNTIME_MODE_MAP 双向:normal ↔ pc-native-0 · legacy ↔ pc-native-legacy · 单测覆盖 - ✅ 切换不破坏链路:SwitchRuntimeAsync 复用 DeviceSwitchRestartCoordinator 模式 · 链路拓扑+参数保持(ADR §3.2 边界铁律 #3) - ❌ PR 自查 1:确认 RuntimeModeService 64 行原五入口都标了 [Obsolete] 注释 · 但功能 100% 保留(grep [Obsolete] 应在 RuntimeModeService 命中 5 次) - ❌ PR 自查 2:确认 WS 同时广播 runtime:switched(新)+ mode_changed(旧)· 7 天宽限期(2026-06-06 之后删旧) - ❌ PR 自查 3:source 控制变量 grep 结果在 commit message 里报告(0 命中 / N 命中 + LEGACY_MAP 处理) - 三元组 trailer 完整 · commit hash 7 位
【commit】
commit subject:feat(P5): RuntimeTarget 7-enum refactor + LEGACY_MAP back-compat (RuntimeModeService [Obsolete] grace 7d) [ADR-AIOS-08 §2.4 fork]
trailer(必须精确): [step=1/1] [pid=P5] [uid=U-runtime-mode-refactor] [occupies=P5.K-services-runtime+P5.K-api-routes+P5.K-controllers+P5.K-ws-broadcast] [files=backend_csharp/Models/Runtime/, backend_csharp/Services/Runtime/RuntimeTargetService.cs, backend_csharp/Services/AudioEngine/RuntimeModeService.cs, backend_csharp/Controllers/Runtime/RuntimeController.cs, backend_csharp/Tests/Services/Runtime/, backend_csharp/Tests/Services/AudioEngine/, backend_csharp/Tests/Controllers/Runtime/, backend_csharp/Program.cs] [ipc=api/runtime/available, api/runtime/switch, ws/runtime:switched(new), ws/mode_changed(deprecated-7d), ws/runtime:get_available, ws/runtime:switch] [legacy-map] LEGACY_RUNTIME_MODE_MAP: normal↔pc-native-0, legacy↔pc-native-legacy (7-day grace until 2026-06-06) [source-grep] grep injectMode/pcInject/dspGen in Services/Source/: <0 命中 / N 命中 + LEGACY_SOURCE_INJECT_MODE_MAP 已落>
回告格式参考 done/P5.U-autotune-batch-pipeline--1898c6f.md 末尾(同部门 · 同 REST + DI 模式)。
【禁止】 - ❌ 不许删除 RuntimeModeService.cs(向后兼容铁律 · 7 天宽限期 · 标 [Obsolete] 但保留 64 行 5 入口) - ❌ 不许在切换时丢失链路状态(ADR §3.2 边界铁律 #3 · SwitchRuntimeAsync 必须保持当前 link 拓扑+参数) - ❌ 不许立即删除 mode_changed WS 事件(双广播 7 天宽限 · 2026-06-06 后由后续 fork 清理) - ❌ 不许引入 .NET 数学库(本任务无数学需求 · 但仍守边界) - ❌ 不许修改 contracts/protocol-v1.md(已 frozen · 留 K2-protocol-v2) - ❌ 不许跨界修改 MetricsAggregator / error_event 路径(同期 P5.U-error-channel + P5.U-perf-service 拥有此区域所有权) - ❌ 不许改前端代码(P1.U-runtime-selector-ui 由 ClaudeA 跑 · 等本任务 zombie) - ❌ 不许改 P6-dsp_algo / pysidecar 代码(DSP 模拟 MVP 仅做 RuntimeTarget 描述 · 真实模拟引擎留后续 fork) - ❌ 不许跳验收 · dotnet build/test 任一红必须修到全绿 - ❌ 不许省略三元组 trailer - ❌ 不许自改本 prompt 文件 ```
解锁链(本任务 zombie 后)
- ✅ P1.U-runtime-selector-ui 解锁(前端 ClaudeA · 1.5d · DrawerEngine RuntimeTarget 下拉 · 切真接口 /api/runtime/{available,switch} + 订阅 runtime:switched WS)
- ✅ ADR-AIOS-08 §2.4 后端侧落地 · AWE Designer 对标完成
- ✅ ADR §3.2 LEGACY_RUNTIME_MODE_MAP 7 天宽限期启动 · 2026-06-06 后由后续 fork 清理 mode_changed
- ✅ K2-protocol-v2 起草时(P_contracts.U-protocol-v2-bootstrap)有 §runtime-selector 实测数据支撑
- ✅ P1.U-link-error-check RUNTIME_PLATFORM_MISMATCH 错误码触发点解锁(本任务 RuntimeTargetService 提供 Kind 判定)
风险评估
| 风险 | 缓解 |
|---|---|
| RuntimeModeService 旧调用方(AudioEngineService 等)在 [Obsolete] 后编译警告污染 | 用 [Obsolete(error: false)] · 仅 warning · 后续 fork 批量清理 |
| SwitchRuntimeAsync 切换中链路状态丢失 | 复用 DeviceSwitchRestartCoordinator 现成"挂起→重启"模式 · 失败自动回滚 _currentTargetId |
| Source 控制变量 grep 命中(ADR §1.3 漏报) | step 1 强制二次 grep · commit trailer 报告 0/N 命中 · 若 N>0 走 LEGACY_SOURCE_INJECT_MODE_MAP |
| TrySetMode 同步包装 SwitchRuntimeAsync(.Result)阻塞死锁 | 用 GetAwaiter().GetResult() · 若 ConfigureAwait(false) · 测试覆盖单线程场景 |
| dsp-simulated MVP 假数据 vs 真模拟引擎 | MVP 仅做 RuntimeTarget 描述(MIPS/Memory/SampleRate)· 真实模拟引擎留后续 fork(ADR §3.2 长期演进 #12) |
| 同期 P5.U-perf-service / P5.U-error-channel commit 冲突 | 三 fork 文件正交:本任务在 Services/Runtime + Services/AudioEngine/RuntimeModeService.cs(独占)· perf 在 Services/Metrics · error 在 Services/Error+Services/Link · Program.cs DI 注册区可能重叠 · 建议串行 push |
历史
| 时间 | 事件 | hash |
|---|---|---|
| 2026-05-30 09:11 | 首次派发(ADR-AIOS-08 accepted v1.1 后第 3 个后端 fork · 用户拍板方向 A 就地扩展 · 本 fork 现状无冲突 · 但需保留 RuntimeModeService 7 天宽限 · 真值核查纠正 ADR §1.3 第二份 RuntimeModeService 路径在 Services/AudioEngine/) | - |