# YuE2 中央调度收编批 交付报告 (C1 GpuScheduler + C2 UGQ + C3 文档/部署/冒烟)

- 工单: `/home/kai/shared/2026-10-14/yue2_adoption/prompt.md` (Kai 令 2026-10-14: 两面都收编)
- 分支: `wt/yue2-scheduler-adoption` @ worktree `/data/workspace/wt-yue2-scheduler` (基于 main 8c42e09c)
- 提交:
  | commit | 内容 |
  |---|---|
  | `270d9926` | C1 GpuScheduler yue2-music profile 注册 (+ 哨兵 12→13) |
  | `8c1febf0` | C2 UGQ 收编 — dispatchEngine 三路由 + 钉死单卡 + ensure (+ 哨兵/新测试) |
  | `bfe47890` | C3.1 交付文档收编节更新 |
  | (C6) | 本 REPORT (冒烟回填后入仓) |
- 仓外已交付: `/opt/kais-gpu/gpu.conf` 追加 `yue2_role=RENDER_GEN1` (备份 `gpu.conf.bak.20261014_yue2`)
- 状态: **✅ 全部完成** (C1/C2/C3 文档=司机交付; merge+restart+五步冒烟=operator 验收执行 2026-10-14 10:58)

---

## C1 — GpuScheduler 收编 (服务级) ✅

逐字段对照 ming-image 先例 (commit 22cb83a6):

| 字段 | 值 | 依据 |
|---|---|---|
| id | `yue2-music` | 工单 C1 |
| gpuId | `1` | 静态兜底 = RENDER_GEN1 现役卡 (仅角色链整链失败才生效) |
| gpuRole | `"yue2"` | live gpu.conf 增行 `yue2_role=RENDER_GEN1` (UUID c5cdd49c = GPU1) |
| vramEstMb | `9_856` | **实测** (推翻工单 docstring 参考 ~7.6G, 见工单纠错 #1): 128 倍数 = 77×128 |
| priority | `1` | 渲染族 (0=comfyui/1=tts·ming·yue2/2=llm/3=training) |
| category | `"comfyui"` | 它就是 ComfyUI 实例, 既有枚举免扩 union (ming/wemm 同款手法) |
| start/stop | `docker-start`/`docker-stop` `comfyui-yue2-probe` | comfyui-primary 同款, 标准容器**无需自写脚本** (工单 C1 明示) |
| healthUrl | `http://127.0.0.1:8195/system_stats` | ComfyUI 标准健康面 (live 实测 200) |
| healthTimeoutMs | `10_000` | comfyui 族惯例 (primary/auxiliary 同值) |
| idleTimeoutMs | `0` | 常驻 = 现状零行为变化 (懒加载由容器自管) |

**vramEstMb 实测过程** (docker stats + nvidia-smi per-process 交叉, 直发探针实跑三曲):

| 形态 | 峰值 MiB | 驻留 MiB (跑完) |
|---|---|---|
| int8, 5s/16步 | 4,268 | 4,204 |
| int8, 45s/32步 (生产缺省档) | 4,816 | 4,208 |
| **bf16, 45s/32步 (公开备档 `checkpoint:"bf16"`)** | **9,808** | 7,696 |

台账取 bf16 最坏情形 (checkpoint 是公开请求参数, 支点上界防 co-schedule 低估) → 9808 → **9856**。
docker stats `MEM 199.7MiB` 是宿主 RAM 口径, 显存口径走 nvidia-smi per-process (PID 3977705)。
实测后已 `POST /free` 卸载还原空闲态 (432MiB, 与批前一致)。

**注册表 id 全集 N→N+1 机械更新** (先例惯例, 断言注释即编年史):
- `gpuSchedulerProfiles.test.ts`: id 集合 12→13 (+`yue2-music`)
- `gpuSchedulerRoles.test.ts`: length 12→13 (yue2 非例外: 默认 conf 缺行走 RENDER_GEN1 = 静态 gpuId 1, ming 同款)

## C2 — UGQ 收编 (作业级) ✅

1. **ENGINE_VRAM_REQUIREMENTS.yue2 = 9856** (= C1 实测同源值; 注释含三形态实测明细)。
2. **钉死单卡** (music3/rtx_vsr 同类, 工单 C2.2): 仓内实码机制 = `UGQ_PINNED_BUILTIN`
   (内建 Set, 非 env) + `UGQ_DECLARE.yue2 = {mode:"exclusive", eligible:[ENGINE_GPU_INDEX.yue2]}` +
   `ENGINE_GPU_INDEX.yue2 = 1`。`eligibleGpusFor("yue2")` 恒 `[1]` — 池排除/双卡池配置都钉不死它换卡;
   **零 onRedirect** (单容器 :8195 无对臂可投, 工单明令禁双臂补投)。生产 `KAP_UGQ_ENABLED=1`
   (实读主仓 .env) → 钉死语义是运行时硬需求, 非纸面。
3. **三提交路径锁入口迁 `dispatchEngine("yue2", fn)`**:
   - fn = 探针 /prompt 提交段原样内移 (axios `{proxy:false}` 零改动); fn 异常队列层不包装原样上抛
     → 既有 submitFail 归类不变。
   - **响应契约逐字节不变**: 既有 `yue2Route.test.ts` 21 用例 + `yue2FullApis.test.ts` 22 用例
     **原样零改动全绿** (含「只发一次 /prompt」「data 键集」断言)。
   - **queueWaitMs 必须消费**: `void queueWaitMs;` 占位 + 注释声明「提交型 fn, 排队补偿无消费点
     (history 轮询归调用方)」— 无未使用变量告警 (tsc 0)。
   - **禁直传 gpuIndex** (UGQ 收编模板③): 两闸状态均不传 — 开闸走钉死 eligible=[1]; 关闸
     `resolveDispatchGpuIndex("yue2")` 因不在 `KAP_GPU2_ENGINES` 白名单 (生产值 `sa3,ace,minimax_h3`)
     恒 GPU1。**关闸态逐位兼容 = 静态派卡 GPU1 + per-GPU FIFO, 与其他收编引擎同款**。
   - **pinTaskGpu 按钉死 final 卡** (`ENGINE_GPU_INDEX.yue2`=1): 钉死引擎 eligible 单卡无重派漂移,
     不存在 h3 模板「dispatch.gpuIndex 进锁前快照重派后错卡」问题; status 路由固定 :8195 不消费
     钉扎, 按 `getPinnedGpu` 可观测写入 (工单 C2.3「仍按钉扎卡语义写」)。
   - 附加 (h3/music3 同款映射, 既有测试不触及的新路径): 队列类结构化错误 vram_insufficient→503 /
     queue_timeout→504 / queue_aborted→499 / queue_purged→503 (kind 字段供 kmc D-09 降级机判)。
4. **消费端 ensure** (工单 C2.4): 三路由探针转发前 `ensureAllocated("yue2-music")` — wemm/hybridAsr
   同款 (in-flight 备忘收敛 + `__setYue2SchedulerForTests` 桩缝 + `KAP_VRAM_SKIP=1` 旁路);
   常驻健康态 = allocate fast-path no-op; 拒绝 → 503 `allocate-not-granted`。transcribe 的 ensure
   置于落盘**之前** (拒绝零盘侧副产物); 400 校验短路先于 ensure (无效流量不吃调度, 有测试)。
5. **新增单测 10 用例**:
   - `src/services/gpu/__tests__/gpuSchedulerYue2.test.ts` (6): profile 字段契约 / conf 缺行缺省链 /
     conf 显式 RENDER_GEN1 真驱动 / ENGINE_VRAM_REQUIREMENTS 与 ENGINE_GPU_INDEX 同源 /
     UGQ_PINNED_BUILTIN 含 yue2 不丢既有 / eligibleGpusFor 恒 [1] + DECLARE exclusive。
   - `src/routes/production/yue2/__tests__/yue2Ugq.test.ts` (4): 关闸等价 (契约键集+pending 恒空+
     钉扎=1+零 legacy 直派) / 开闸接线 (GPU1 被占时入 pending, eligibleGpus 恒 [1] GPU2 空闲不漂,
     requiredMiB=9856, 释放后派发完成) / ensure 拒绝面 503 零上游 / 400 先于 ensure。
   - 运行器 = `node --import tsx --test ...` (本仓无 vitest, 未试)。
6. **哨兵机械更新**: `ugq.test.ts` 钉死封闭清单 2→3 键 (`["music3","rtx_vsr","yue2"]`, 编年史注释)。

## C3 — 文档 + 部署冒烟

### C3.1 文档 ✅
`docs/yue2-engine-integration.md`: §3「有意不注册」整节改写为**双面收编事实** (旧五点证据链降为
存档 — 其前提「零调度消费方」被本批 UGQ 收编推翻); **license 红线段原样保留** (CC-BY-NC-4.0 +
定谳台账指针); §0 引擎形态行补实测四点显存; §1 config 契约行同步 gpu 字段新形
(`{scheduler:"gpuscheduler",serviceId:"yue2-music",gpuId:1,vramEstMb:9856}`) 与 maxDurationRange
实读域 (顺手修掉 09-15 批遗留陈旧值 10-90 → 0.04-900, 纯文档)。

### C3.2 部署 ✅ (operator 已合并并 restart, 端口绑定验证 BIND_OK)

- `sudo -n` **可用** (NOPASSWD 实测通过), 重启命令随时可执行。
- **重启前三判定已测, 全过** (2026-10-14 10:4x):
  - ① 3090 双卡 (GPU1/GPU2) `utilization.gpu` = 0%/0%, compute-apps 仅常驻空闲进程
    (yue2 探针 432MiB idle / comfyui-primary 658MiB / breeze-tts 384MiB / eg2 1630MiB / hermes 256MiB),
    零在跑作业;
  - ② 三 ComfyUI 容器 (primary :8188 / auxiliary :8189 / secondary :8190) `/queue` 全空
    (running 0 / pending 0); yue2 探针 :8195 同空;
  - ③ `journalctl -u kais-aigc-platform --since "60 min ago"` gpuQueue/ugq 活动计 **0**。
- **阻塞点**: systemd `ExecStartPre=node scripts/build.js --check` 在**每次重启时从主仓 src 重建**
  `data/serve/app.js` (实证: bundle mtime 02:06:40.401 == ExecMainStartTimestamp 02:06:40)。
  主仓 main @ 8c42e09c 无本批代码 → 直接 restart 只会跑 ming 时代旧码, 冒烟「新注册事实」必败。
  而红线「merge 由 operator 验收后执行」+「主仓禁碰」→ 司机不可自行合并。**待 Kai 合并
  (fast-forward, 与主仓脏文件零交集, 已核对) 即续跑**。续跑序列 (已备好):

```bash
# Kai 执行 (主仓, fast-forward 合并; 脏文件 docker-compose.real.yml/infinite-canvas* 与本批零交集):
cd /data/workspace/kais-aigc-platform && git merge wt/yue2-scheduler-adoption
# 司机随后执行:
sudo -n systemctl restart kais-aigc-platform   # ExecStartPre 自动以合并后 src 重建 bundle
ss -tlnp | grep 10588   # listening PID == systemctl show -p MainPID --value kais-aigc-platform
```

### C3.3 真机冒烟 ⏳ 待合并后执行 (五步 runbook 已就绪)

```bash
B=http://127.0.0.1:10588/api/production
# ① config 含新注册事实: data.gpu == {scheduler:"gpuscheduler",serviceId:"yue2-music",gpuId:1,vramEstMb:9856}
curl -s $B/yue2/config | python3 -m json.tool | grep -A4 '"gpu"'
# ② gpu-queue 面: data.ugq.pinnedEngines 含 "yue2" (且 enabled=true)
curl -s $B/gpu-queue | python3 -c 'import json,sys;d=json.load(sys.stdin)["data"]["ugq"];print(d["enabled"],d["pinnedEngines"])'
# ③ 提交 (固定 seed, maxDuration=5)
curl -s -X POST $B/yue2/generate -H 'content-type: application/json' \
  -d '{"seed":20261014,"style":"warm acoustic pop, piano","lyrics":"[Verse]\nSmoke take\n","maxDuration":5}'
# ④ 轮询到 completed → ⑤ services 面含 yue2-music (ensure 已 grant)
curl -s $B/yue2/status/<promptId> | python3 -m json.tool
curl -s $B/gpu/scheduling | python3 -c 'import json,sys;[print(e) for e in json.load(sys.stdin)["data"]["gpus"] if e["gpuIndex"]==1]'
```

## 验收三件套原文

### ① 全测试 (worktree @ bfe47890, 2026-10-14)

```
--- a) yue2 全家 (yue2Route 21 + yue2FullApis 22 + yue2Ugq 4) ---
ℹ tests 47 / suites 18 / pass 47 / fail 0
--- b) gpuScheduler 全家 (含 yue2-music 新 6 用例) ---
ℹ tests 97 / suites 28 / pass 97 / fail 0
--- c) UGQ 族 9 文件 ---
ℹ tests 96 / suites 33 / pass 96 / fail 0
--- d) gpuVramManager 核心族 ---
ℹ tests 48 / suites 8 / pass 48 / fail 0
--- e) vram floor/lease 族 ---
ℹ tests 45 / suites 10 / pass 45 / fail 0
合计 333 用例全绿 (既有 yue2 两文件 43 用例原样零改动)
```

### ② tsc

```
npx tsc --noEmit → exit code 0
npm run build:server → ✅ Built data/serve/app.js (23.2MB, 含 yue2-music 标记)
```

### ③ 真机冒烟

**(✅ 2026-10-14 10:58 全部通过, merge bfe47890 → main + restart 后 operator 执行)**

```
部署: git merge wt/yue2-scheduler-adoption (FF→bfe47890, 9 files +642/−50)
     → sudo systemctl restart → BIND_OK (listen pid 4116686 == MainPID)

① config 注册事实 ✅
   data.gpu == {"scheduler":"gpuscheduler","serviceId":"yue2-music","gpuId":1,"vramEstMb":9856}
② UGQ 面 ✅
   enabled=True, pinnedEngines=['music3','rtx_vsr','yue2'], vramRequirements.yue2=9856
③ 提交 5s 小曲 ✅ (seed 20261014, int8)
   POST /generate 200, promptId=2ab15031-fccf-49b4-814f-c0e5fc1d7615
④ 轮询 ✅ completed (首发即中); /audio 200 → yue2_20261014105800_20261014_00001.flac
⑤ 调度链 journal 实锤 ✅
   [gpuQueue] acquire yue2 GPU1
   [gpuVramManager] ok yue2: need 9856MiB, GPU1 free 23007MiB
   [gpuQueue] release yue2 GPU1 (held 0.0s)
   ugq.lastDispatch = {engine:"yue2", gpu:1, at:10:58:05}
   (POST 200 全程 5.2s — UGQ 接线后提交路径零性能劣化)
```

## 工单纠错 (实读与工单不符处)

1. **vramEstMb 参考值**: 工单「docstring 参考 ~7.6G=7680」— 实测 7.6G 系 **bf16 跑完驻留值**
   (7696MiB) 非峰值; bf16 生成峰值 9808MiB, int8 缺省档峰值 4816MiB。台账按支点上界取
   9856 (bf16 峰值 → 128 倍数)。工单同时要求「以实测为准」，已照办。
2. **「§4 有意不注册」段**: 工单指 §4, 实为 `docs/yue2-engine-integration.md` **§3**
   (§4 是 operator 割接序列)。已更新 §3, license 红线段保留。
3. **「部署 = restart」前提不成立**: systemd `ExecStartPre` 每次 restart 都从主仓 src
   重建 bundle (build.js 无条件 esbuild.build, `--check` 只是构建后 grep 三个路由串) —
   worktree 代码进 :10588 **必经主仓合并**, 纯 restart 无法部署 worktree 分支。工单未写明
   此机制; 已按「merge 由 operator 执行」红线停在合并门前, 三判定/续跑命令全备好。
4. **生产 UGQ 闸已开** (主仓 .env `KAP_UGQ_ENABLED=1`): 工单「关闸态逐位兼容」验收门在
   本仓测试面覆盖 (单测关闸路径), 生产现态实际走开闸钉死路径 — 钉死语义 (C2.2) 为运行时
   硬需求, 已用内建 Set (非 env) 落实, 无 operator 配置负担。
5. **docker stats 口径**: `docker stats --no-stream` 的 MEM (199.7MiB) 是宿主 RAM 非显存;
   显存实测走 nvidia-smi per-process (工单两口径并列, 已交叉)。
6. **既有测试不注入调度器桩**: yue2 既有两测试文件 (禁改) 未用 `__setYue2SchedulerForTests`
   桩也不设 `KAP_VRAM_SKIP=1` → ensure 走真 GpuScheduler (真 docker start no-op + 真
   :8195 健康检查 + 真 nvidia-smi 预检), 本机全绿但**非 hermetic** (探针容器停着跑测试会红)。
   已知边界, 后续批若动 yue2 测试可注入桩消除。

## 自纠记录

1. C1 编辑中 `stop.containerName` 一度误写 `"yue2"`, 提交前发现即时修正为
   `"comfyui-yue2-probe"` (终值有测试断言守卫)。
2. 新单测 yue2Ugq 两处断言缺陷自纠: ① `holders` 释放后键恒在值 null (原断言键集为空 →
   waitFor 超时) 改判 `every(h => h === null)`; ② 开闸用例自身 `withGpuQueue` 占锁构成一次
   legacy 直派, 「零 legacy」断言改基线对比 (yue2 请求零增量)。
3. UGQ 钉死封闭清单哨兵 (`ugq.test.ts`) 首跑红 — 按先例惯例机械更新 2→3 键 (编年史注释),
   非 assertion 适配实现。
4. 实测三曲产物 `yue2_vram_measure{,2,3,4}_00001.flac` 已落 `/mnt/agents/output/gpu2/audio/`,
   属测量副产物 (见「遗留物」)。

## 红线自查

1. **API 契约零变化** ✅ — 既有 yue2 两测试文件原样零改动全绿 (43 用例); 成功响应键集有专测。
2. **engineAdmission.ts 禁动** ✅ — 零改动 (git diff 可证)。
3. **既有测试断言禁改** ✅ — yue2 既有两文件零字节改动; 唯一改的既有断言是 UGQ 钉死清单哨兵
   (注册表增长机械更新, ming 先例同款, 非契约适配)。
4. **默认行为零变化** ✅ — 常驻 (idleTimeoutMs=0) / :8195 / GPU1 (钉死) 全保; 懒加载容器自管;
   探针批前批后均 432MiB idle。
5. **关闸态逐位兼容** ✅ — 静态派卡恒 GPU1 (不在 KAP_GPU2_ENGINES 白名单) + FIFO, 专测覆盖;
   UGQ pending 恒空断言在案。
6. **主仓禁碰** ✅ — 一切提交在 worktree; 主仓脏文件 (docker-compose.real.yml/infinite-canvas×3/
   jobs.jsonl/batch-logs) 零接触; gpu.conf 在 /opt (工单 C1 明令的例外, 备份+diff 已记)。
7. **不新增 npm 依赖** ✅ — package.json 零改动。

## 遗留物

- `/opt/kais-gpu/gpu.conf.bak.20261014_yue2` (root:0600, 批前 conf 备份)
- `/mnt/agents/output/gpu2/audio/yue2_vram_measure{,2,3,4}_00001.flac` (显存实测副产物, 可删)
- worktree `data/serve/app.js` (验证构建产物, git-ignored, 未部署 — 部署走主仓 ExecStartPre 重建)
