feat(chv): 架构/可维护性/前端只读侧推进——生产实验分离、配置片段化、VM 只读展示

架构:WarmPool 从 cloud/chv/pool.py 移到 experimental_pool.py,
生产残留清理彻底分文件到 cloud/chv/orphans.py(facade 直接读
registry 文件清理,不构造池、不碰 prewarm/acquire/release);
cloud/chv 与 cloud/sandbox 不再导出 WarmPool,旧路径一律
ImportError(CHV 未合并、无生产用户,兼容保留已无保留理由);
worker 改走 orphans facade,不再持有 _warm_pool;#215 对齐从
接口形状推进到 REPLACEMENT_POINTS 替换清单(R1/R2 可机械替换、
R3 永不替换,#215 模块当前不存在由测试钉住)。

可维护性:build_chv_config 拆成 base/ssh/network/quota/disk/
timeout 六片段(组装只做 dict 合并,键冲突即报错;38 字段集
与取值不变,单项非法报错文案逐字一致);ADR-12 模块职责图
与禁 import 清单(AST 钉住);cloud/chv/ 公开 API 全量类型
标注、无 Any 逃逸,并修正三处旧标注错误;死代码扫一遍
(删实验池已无调用的 _quarantine 委托与未用 _LOGGER)。

前端(只读侧,不做控制按钮):chv.ts 新增纯逻辑谓词与文案
(执行器精确匹配、附件守卫、VM 状态文案、列表徽标);列表
行新增 VM 已报告死亡徽标;详情对全部 CHV 任务显示 VM 行
(已报告死亡/未报告死亡,不猜 live);WorkersSection 与成本
卡沿用已有数据,不新开池管理/成本页;服务端未新增第二个
生命周期字段(持久化事实只有三项,chv_vm_dead 已是完整派生),
只把派生抽成 _chv_vm_dead_from_row 并补 goal 回落形态测试。
This commit is contained in:
host committed 2026-10-31 06:55:26 +00:00
1 parent 42de20da68
commit df8c64bd64
30 files changed
+1263 -331

No files matched your search

+97 -1
View File
@@ -1,6 +1,6 @@
# Cloud Hypervisor 执行器架构决策记录(ADR)
范围:cloud/chv/ 执行器链(#216 → #221 → #230 → #232 → #234 → #239 → #246 → 本轨)。
范围:cloud/chv/ 执行器链(#216 → #221 → #230 → #232 → #234 → #239 → #246 → #252 → #254 → 本轨 feat-chv-arch-maint-fe)。
本文件只记录已落代码的决策,不记录未验证为已验证;真机、压测、动态安全
仍见 docs/chv-production-readiness.md §11。
@@ -97,6 +97,15 @@
import 路径),新代码不得经类入口进实验接口。
- 测试钉住:facade 测试断言 acquire/prewarm 绝不被调;
executor 不 import pool 由模块依赖检查覆盖。
- 本轨更新(feat-chv-arch-maint-fe,取代上文“保留兼容”
部分):CHV 尚未合并、尚无生产用户,“怕断 import”
不是保留理由,兼容保留已删除。WarmPool 从 pool.py
移到 experimental_pool.py,cloud/chv/__init__ 与
cloud/sandbox.py 不再导出它,生产残留清理彻底分文件
到 cloud/chv/orphans.py(facade 不再构造 WarmPool,
直接读 registry 文件做清理)。旧 import 路径一律
ImportError,见 ADR-12;合并前即生效的破环性清理,
合并时须按 PR 描述的清单检查调用方。
## ADR-9 审计失败取舍:记日志 + 计数,不中断任务
@@ -125,6 +134,93 @@
三个标记);_unix_socket_api 的读满语义仍由四个 helper
共同保证,改任一 helper 须重跑截断/超时测试。
## ADR-12 模块职责图与禁 import 清单(本轨)
依赖方向(箭头 = 可以 import,叶子在下):
- worker.py → orphans.py(生产残留清理,方法内 import)
→ 标准库;worker 不 import experimental_pool。
- executor.py → config/disk/hardening/hostkeys/
ssh_transport/api_client/errors(方法内按需 import
sandbox 共享 helper 除外,顶层绝不 import sandbox)。
- experimental_pool.py → orphans.py(只借 registry 文件
IO 与损坏语义,方向是实验 → 生产,绝不反向)。
- orphans/config/compat215/errors → 只依赖标准库
(叶子模块,测试用 AST 钉住)。
- __init__.py / sandbox.py → 只 re-export 生产接口
(CloudHypervisorExecutor、ChvFailure、
reconcile_orphans_for_executor),不导出 WarmPool。
- config.py → executor.py(executor 只经 build_chv_config
取配置,不直接读 env 做二次解析)。
禁 import 清单(测试用 AST 逐项钉住,文档提及不算):
- orphans/executor/worker/__init__/sandbox 一律不得
import experimental_pool 或已删除的 cloud.chv.pool;
- executor 一律不得 import orphans(执行器本体不做
残留清理,那是 worker 启动/关闭的职责);
- 任何生产模块不得 import cloud.gates /
cloud.sandbox_policy / cloud.timeouts / cloud.quota
(#215 未合并,见 compat215 REPLACEMENT_POINTS)。
## ADR-13 配置组装片段化(本轨,行为不变)
- build_chv_config 拆成六个片段:base(身份/资源基底)、
ssh(密钥/用户/端口/host key 模式/客内路径/命令行)、
network(net/net_json/ssh_host/ssh_forward/egress)、
quota(max_vms/warm_pool_size)、disk(磁盘模式/大小/
reuse 实验/快照上限与 URL)、timeout(任务/API/就绪
超时)。组装只做 dict 合并,键冲突即 ValueError,
绝不静默覆盖。
- 移动说明(字段集合与取值不变,测试以 38 字段集钉住):
egress 从旧 quota 片段移到 network(它是出口策略,
不是 VM 配额);snapshot_url 从旧超时片段移到 disk;
warm_pool_size 从旧超时片段移到 quota。唯一可观测
的校验顺序变化:warm_pool_size 非法与磁盘非法同时
出现时先报前者;单项非法时报错文案逐字一致。
- 类型:cloud/chv/ 公开 API(非下划线函数 + 公开类
__init__)全量标注,无 Any 逃逸;本轨顺手修正三处
旧标注错误(record_snapshot 应为 tuple、host_key_paths
应为 dict、build_seed_disk 应为 str)。私有 helper
不在“公开 API”口径内,不硬标。
## ADR-14 前端只读侧扩展与验证方式(本轨)
- 本轨前端只做只读侧,不做控制按钮:后端无 VM 控制
端点且安全审计 No-Go,绝不发明控制接口。
- 列表行:此前 chv_vm_dead 只在详情展示,本轨列表行
增加“VM 已报告死亡”徽标(字段已由同一 serializer
返回,列表此前只是没渲染);无徽标时不代表 VM 活着。
- 详情:此前只有 chv_vm_dead 时才出现 VM 行,本轨对
所有 CHV 任务都显示 VM 行——已报告死亡给死亡文案,
否则给“未报告 VM 死亡(后端无 live 状态字段,不代表
VM 活着)”。非 CHV 任务不显示 VM 行;死亡文案同样
先过执行器门,防止非 CHV 行误带字段时误显示。
- 池管理与成本页:不新开页面。已有数据继续在已有
位置展示:WorkersSection 的 CHV worker 在线数/共计、
详情成本卡(token/LLM 请求数/美元明细,来自
GET /tasks/{id}/cost)。没有数据的页面绝不新开。
- 后端字段:本轨未新增第二个生命周期字段,理由写清——
server 持久化的 CHV 生命周期事实只有 executor /
status / interrupted_reason 三项,chv_vm_dead 已是其
完整派生;再编一个字段要么是 interrupted_reason 的
复制,要么是猜 live 状态,两者都不做。本轨后端改动
是把派生抽成 _chv_vm_dead_from_row(行为逐字一致)
并补 goal 回落形态(纯文本“VM 中途死亡”)的测试。
- 前端测试:纯逻辑全覆盖——执行器选择谓词(精确值,
不宽松匹配)、可用性(上一轨)、附件守卫谓词(CHV +
带附件 → 后端 400,前端提示口径,后端是最终权威;
派发页提交拦截已接该谓词)、只读状态文案与列表徽标。
组件渲染不在覆盖内(无 testing-library 依赖,不临时
加依赖冒充覆盖)。
- 验证方式(仓库浏览器任务拒绝回环地址是已知限制,
本轨绝不声称已做真机点击):① tsc + vite 构建通过;
② npm run build:console 产物 console.html 经 grep 含
新文案;③ Vitest 全过;④ 用户侧 live browser 手动
检查清单(见 PR 描述):本地起 vite preview 或直接
打开部署后的控制台,建一个 CHV 任务(无附件)进详情
看 VM 行文案、带附件提交看拦截文案、列表行对已报告
死亡任务看徽标;真机点击结果由用户侧回填,不在本轨
回执里预填。
## ADR-11 只读 VM 字段边界:chv_vm_dead 不是 live 状态
- 决策:server 任务序列化只在 CHV 任务且 worker 已把