Commit Graph
501 Commits
Author SHA1 Message Date
host b923d0291b fix(chv-muse-r12fix-1): /run/chv 改为组 chv-browser 可写(P4 broker 权限)
R12 真机:/run/chv root 所有 0755,chv-browser 建不了
cdp-broker.sock,chv-cdp-broker bind 报 PermissionError 而
crash-loop,P4 在交付镜像上永远起不来。改为 2775 root
chv-browser(setgid,沿用真机手工修复 chgrp + chmod 2775
后全过的口径);绝不用 0666/0777 放宽,与 round-11 authd
socket 组权限 0660 同款口径。建目录时的 chmod 与 tmpfiles
登记两处同改。
2026-11-08 04:23:41 +00:00
host dafb0c6606 fix(chv-muse-r12fix-0): build.sh 建 /usr/local/share/chv 目录(认领 c3bf9fd)
纯 114b444 建不出镜像:P4 步骤把 browser-blocklist.txt 拷进
/usr/local/share/chv/,脚本里没有任何 --mkdir,virt-customize
报 target is not a directory。R12 真机靠本地-only 提交 c3bf9fd
(1 行,未推未合)绕过;本提交把该行合回分支,此后测试镜像
须从已推分支纯建出来。
2026-11-08 04:23:41 +00:00
host 114b444554 fix(chv-muse-round12): 移植 round-11 的 Page.aria_snapshot() 纠正到 CDP broker
Playwright 1.64.1 没有 page.accessibility(Puppeteer 的 API),
broker 的 snapshot 调它必走异常分支。改调 Page.aria_snapshot()
(字符串);guard 的 truncate_a11y 换成字符串版 truncate_aria
(179a86d 原样),detect_injection/detect_form_risk 加字符串
分支(dict 分支保留);两边 fake 与 P4 文档同步改。a11y-only
契约不变:不读原始 DOM、不走 JS eval。

测试:test_chv_muse_p2 + test_chv_muse_p4 114 OK;全量 2523 OK
(skipped=10)。
2026-11-07 18:24:58 +00:00
host 9da74c2a78 merge(chv-muse-round12): 合入 round-11 修复轮 HEAD 50cae2a (RETEST6 全绿)
按 hunk 合入,不整文件覆盖。冲突 3 处:
- chv-guest/build.sh:取 P4 的 copy-in(cdp-broker/guard/blocklist/
  chv-memory)+ cdp-broker service/enable 三件;取 R11 的 git
  config(去掉 credential.helper store,层 5);tmpfiles 两边
  并存(/run/chv/ask-queue + /run/chv-browser)。
- chv-guest/chv-browser.py:P4 架构保留(CDP broker 客户端 +
  guard 模块);取 R11 两处仍适用的运行期修复:BROWSER_SOCK
  移进 /run/chv-browser(缺陷 4,合入后的 broker 已指该路径)
  与 handle_request 认 op=browser/action 透传;R11 的直连
  Playwright 代码(validate_url/truncate_aria/_load_playwright/
  _page snapshot)由 P4 取代,不带回。
- cloud/tests/test_chv_muse_p2.py:truncate 三测取 P4 的
  GUARD.truncate_a11y(dict 树);FakeCdpBroker 取 P4 版,删
  R11 的 FakePage 系列;干跑夹具取 R11 的 _fixture 调用,并
  给 _fixture 补 P4 四件(否则合入后 build.sh fail-fast)。

合入内移植(R11 修复在 P4 里移了位置,不移植即静默丢失):
- chv-guest/chv-cdp-broker.py:_load_playwright 返回
  sync_playwright()(原 R11 在 chv-browser.py 修过的同形
  bug);回归测试由 test_p2 移植到 test_chv_muse_p4.py。
- test_chv_muse_p2.py:HOST 由模块级 eager 改懒加载(_host()):
  host.py 顶层 import queue 会把 stdlib queue 缓进 sys.modules,
  使同进程后加载的 test_queue_sandbox 取不到 cloud/queue.py
  遮蔽而 ImportError(全量唯一失败)。

未移植(见报告):R11 的 Page.aria_snapshot() 纠正未进
chv-cdp-broker.py(仍调 accessibility.snapshot(),且 guard
的 dict 树 API 须配套改,属 P4 返工,不在本轮合入做)。

全量 cloud 套件:2522 tests OK (skipped=10),基线 a6f9597
为 2474 OK (skipped=12);+48 来自 R11 新增测试(p2 +36、
p3 +7、closeup +3、gaps +1、p4 +1 移植)。
2026-11-07 18:13:31 +00:00
host 50cae2a0af part 6 修复:worktree 根移出 /app——RETEST5 挂载点回归
RETEST5:part 5 的 --bind=<workspace>/.chv-worktrees:/app/work
(adc7bf6,排在 --bind-ro=/app 之后)让所有 cell 启动失败:
后挂的 bind 只有在挂载点已存在时才能盖住,挂载点不存在时
nspawn 在只读 /app 下连建都建不起来(Failed to create mount
point .../app/work: Read-only file system,全量 73 次,
CELL-RC=0 共 0 次),缺陷 1 的 15/15 回退为 0/15。

三选一取 (a) worktree 根移出 /app:
- chv-cell 不再发任何指向 /app 之下的 bind,删
  --bind=...:/app/work 与 worktrees_dir 参数;WORKTREE_DIR
  (/home/chv/workspace/.chv-worktrees)改经
  --setenv=CHV_WORKTREE_ROOT 传进单元——该路径在已可写绑进
  单元的 workspace 之下,挂载点问题按构造即不存在;backing
  仍在客机 workspace 下随磁盘持久化,隔离与落盘语义不变,
  只是单元内路径从 /app/work/host-worktrees 改为
  /home/chv/workspace/.chv-worktrees/host-worktrees。
- host.py 新增 worktree_base():CHV_WORKTREE_ROOT 优先;
  CHV_CELL=1 但变量未设时回落到同一单元内路径(旧版 chv-cell
  / 单元内手动跑 host.py 仍不出 /app);单元外沿用
  HERE/work/host-worktrees,行为不变。make_worktree 与
  dispatch_from_branch(第二个 HERE/work 调用方)同走该函数,
  goal/fork/依赖链一并修到。
- 不选 (b) 默认 --no-worktree:隔离丢了,且 goal/fork 同样
  调 make_worktree,照样撞 Errno 30;不选 (c) 镜像预建空
  目录 /app/work:修法耦合镜像构建,某次重建漏建即静默回归,
  且单测无法钉住镜像内容。
- 根因 1(39084ce)与根因 3(0203f32)代码未动。

单测:删 part 5 钉 argv 顺序的 4 项(该顺序本身已被真机证伪),
新增 CellPart6WorktreeTest 4 项 + HostWorktreeBaseTest 5 项:
argv 里无任何指向 /app 之下的 bind、CHV_WORKTREE_ROOT 经
setenv 进单元、worktree 根只经可写 workspace 绑定可达、
两处同值常量一致、worktree_base 三路选择、make_worktree
真 git worktree 落点、dispatch_from_branch 同根。

全量:cloud/ 下 discover -s tests,2067 项 OK(skipped=9)。
(注:仓库根跑 discover 会把 queue 解析成标准库、致
test_queue_sandbox 导入失败,须在 cloud/ 下跑。)
2026-11-07 14:23:48 +00:00
host 0203f32627 part 5 修复 3:P2-9 degraded_logged=False——降级日志文案漂移
定位结论(已定位,不是时序、不是日志路径变化):part 4 提交
7a22732 把 serve() 从 peer_allowed 切到 peer_allowed_multi 时,
降级分支的 reason 与警告行都被改写成了“无 cell pid 文件”,
“cell.pid 缺失”这个串自此再也没进过 journal——P2-9 驱动
grep 'cell.pid 缺失' 自然命中不了,RETEST4 的
degraded_logged=False 即由此来。RETEST3 同项为 True,是因为
当时跑的还是 part 4 之前的 broker(serve 直接打印含该串的
警告行)。降级 ping 本身一直 ok=True,降级逻辑没坏,坏的是
日志里的认领字符串。

修复:peer_allowed_multi 无 pid 文件时的 reason 改回与单 cell
版 peer_allowed 完全一致的“uid 在范围内(cell.pid 缺失,未做
pid ns 对照)”;serve() 的警告行改走新 helper
_degraded_warning(reason, pid, uid),把 reason 原样打进日志
(不再手写第二份文案,避免两者再次悄悄漂移;今后改文案只改
reason 一处)。journalctl grep 'cell.pid 缺失' 恢复命中。

新测试 cloud/tests/test_chv_muse_p2.py::BrokerMultiTest(2 项):
reason 含“cell.pid 缺失”、警告行含该串且带 pid/uid。
2026-11-07 12:07:32 +00:00
host adc7bf6666 part 5 修复 2:默认 dispatch 在只读 /app 下建不了 worktree——workspace 绑定 /app/work
RETEST4 新发现 2:make_worktree 把 worktree 建在 HERE/work/
host-worktrees,单元内 HERE=/app 而 /app 是 --bind-ro,带 repo
的默认 dispatch 撞 OSError Errno 30(--no-worktree 的同任务
越过此关、跑到真实 turn)。

二选一取 (a) 给 worktree 一条单元内可写路径:客机 workspace
下的 .chv-worktrees 以可写 --bind 挂到单元内 /app/work 上
(build_nspawn_argv 新增 worktrees_dir 参数,默认
WORKTREE_DIR=/home/chv/workspace/.chv-worktrees;run_cell 起
单元前 _ensure_bind_source 建好该目录并 chown 回 chv)。
取舍:
- 不选 (b) 执行器默认 dispatch 带 --no-worktree:worktree
  隔离就丢了——任务改动直接落进 repo 本身,多任务同仓互撞,
  merge/discard/seed-dir 语义全坏;且只修 dispatch,goal/fork
  同样调 make_worktree,照样撞 Errno 30。
- (a) 内不选可写 tmpfs:tmpfs 随单元退出即丢 worktree,跨
  cell 的 run/merge 续跑找不着工作区;workspace 绑定随客机
  磁盘持久化,worktree 语义、隔离与落盘位置(仍是
  /app/work/host-worktrees/<任务>)全都不变,也不碰 P2 的
  /usr 只读约束。
- bind 必须排在 --bind-ro=/app 之后(nspawn 按顺序挂载,后挂
  的才盖住只读子树下的子路径),单测钉住该顺序。

未决(须 RETEST5 真机验证):nspawn 对 --bind 目的路径的
处理(目的不存在时是否自动建挂载点)与挂载叠加顺序只能在
真机上验证,单测只能钉住 argv 形状;本轮不建镜像、不跑真机。

新测试 cloud/tests/test_chv_muse_p2.py::CellPart5WorktreeTest
(4 项):可写 bind 在位且在 workspace 下、顺序在
--bind-ro=/app 之后、自定义目录参数生效。
2026-11-07 12:07:01 +00:00
host 39084ce2bf part 5 修复 1:单元内 health 恒 false——default_codex_bin() 回落 shutil.which
RETEST4 新发现 1:单元内无 CODEX_BIN、无 HERE/bin/codex 时
default_codex_bin() 返回裸 "codex",run_health_check 的
os.path.exists("codex") 以相对路径判恒为 False,health 的 ok
恒 False(execpath4、p2_7v4、P1 resident_health、P1B 两 call
全因此失败;定界探针证实单元内 which codex 就是
/usr/local/bin/codex,加 CODEX_BIN 前缀即 rc=0 且 ok=true)。

三选一取 (c) 回落 shutil.which("codex"):
- (a) CODEX_BIN 进 FORWARD_ENV_ALLOWLIST:要执行器与 chv-cell
  两边接线、还得在客机环境里定一个单元内路径,改动面大,且
  只修执行器驱动的调用,直接跑 host.py 的 health 修不到。
- (b) _guest_host_command 显式加 --codex-bin:把客机镜像路径
  硬编码进执行器,同样只修执行器驱动的调用。
- (c) 单点修 host.py:health、Server、automations 全部调用方
  直接受益;PATH 里找得到即返回绝对路径,找不到仍返回裸
  "codex"(行为与旧代码一致,零回归面);不硬编码任何路径。

新测试 tests/test_host.py::DefaultCodexBinTest(4 项):模拟
单元内环境(PATH 含 codex、cwd 不含 codex、无 CODEX_BIN)
断言返回绝对路径、CODEX_BIN 优先、处处无 codex 时仍返回裸
"codex"、以及该解析下的 run_health_check ok=true。
2026-11-07 12:06:20 +00:00
host c4661b64db part 4 文档:P2 差距表补记三缺陷修复
RETEST3 三个 cell 新缺陷的修复方向与实现位置记入 §6:
起步门 + init pid 轮询、volatile 空根只读绑(/app、CA、
单元专属 resolv.conf/hosts;不绑 nsswitch.conf 的原因)、
唯一身份分配 + broker 多 cell 对照、执行器记 body。
2026-11-07 10:30:45 +00:00
host f20cbc79cd part 4 附带:vm.boot/vm.create 500 记响应体
_boot_vm 只留 status 导致间歇 500 根因无法定位(两轮已提,
未改)。_api_call 的两个调用点现在都把响应体(截断到 500
字符单行)放进 ChvFailure 消息:vm_create_failed 与
vm_boot_failed 均含 status 与 body。body 是 VMM 的报错文本,
不含秘密;超长截断并标明。
新测试:test_wake_vm_boot_failed_records_body、
test_wake_vm_create_failed_records_body、
test_wake_vm_boot_failed_body_truncated。
2026-11-07 10:30:45 +00:00
host 7a227323f2 part 4 缺陷 3:并发 cell 撞毁——唯一身份 + run_cell 接线
RETEST3 缺陷 3:固定 ve-chv-cell 名 + 固定 10.200.0.1/24,
第二个 cell 在 nspawn 建 veth 时即 File exists/Address
already assigned。
- CellNet + allocate_cell_net():每次 run_cell 在 flock 锁下
  (/run/chv/.cell-alloc.lock,30 秒超时)分配唯一身份:
  machine chv-cell-<xx>(11 字符)、宿主 ve-chv-cell-<xx>
  (14 字符,接口名 15 字符上限内)、10.200.<xx>.0/24;
  扫描活接口 + 地址 + machinectl 在册名,避让注销滞后的下标。
- 配网命令表(host/cell/cleanup)与 build_nspawn_argv 接受
  net/cell_net 参数;None 时仍用旧常量(argv 子命令与旧测试)。
- run_cell 重写:分配身份 -> 生成专属文件 -> 起步门 payload
  -> 等 veth(20 秒)-> 宿主配网 -> 轮询 init pid(10 秒)
  -> 写 pid 文件 -> 单元内配网 -> 开门 -> 等退出码;
  nspawn 已死返回其退出码;finally 清 NAT/pid/专属文件。
  共享网络模式跳过门与身份(无配网可等)。
- broker:pid ns 对照改多 cell 版(peer_allowed_multi +
  _read_cell_pids:cell-<machine>.pid glob + 兼容旧 cell.pid;
  对端 ns 与任一活单元同 ns 即放行;残留 pid 文件只跳过,
  全部读不到才拒绝,不降级)。
新测试:CellAllocTest、CellPart4NetworkTest、
CellPart4ArgvTest(cell_net 两法)、BrokerMultiTest。
2026-11-07 10:30:30 +00:00
host 2be69af92c part 4 缺陷 2:volatile 空根缺 /app 与 /etc 关键文件
RETEST3 缺陷 2:--volatile=yes 下单元根是基本空的 tmpfs,
/app、/etc/resolv.conf、/etc/hosts、/etc/ssl/certs 全无,
执行器单元路径一律 ENOENT、DNS/TLS 不可用。
- build_nspawn_argv:固定只读绑 --bind-ro=/app 与
  --bind-ro=/etc/ssl/certs;新增 extra_ro_binds 参数
  (单元专属 resolv.conf/hosts 由 run_cell 传进来)。
- prepare_cell_files():按客机有效 DNS 生成单元专属
  resolv.conf/hosts(/run/chv/resolv.conf.<machine>、
  hosts.<machine>,0644——单元 root 映射为客机 uid 1000,
  0600 的 root 文件单元内读不到;DNS/hosts 不含秘密)。
  客机 resolv.conf 可能是 systemd-resolved stub
  (127.0.0.53),单元内无 resolved,直接绑 stub 即 DNS
  全灭,故过滤 stub 后备选 /run/systemd/resolve/resolv.conf,
  两处都没有即大声失败。nsswitch.conf 故意不绑(客机版
  可能含 resolve 模块;glibc 默认 files+dns 正是单元需要的)。
新测试:CellFilesTest、CellPart4ArgvTest(绑定两法)。
run_cell 接线在缺陷 3 的重写里做。
2026-11-07 10:29:31 +00:00
host fc33eed262 part 4 缺陷 1:cell 启动竞态——起步门 + init pid 轮询
RETEST3 缺陷 1:run_cell 里配网与 payload 零同步,快 payload
(run -- true)在配网完成前退出即撞墙,11 次只成 6 次。
本提交先落 helper(接线在缺陷 3 的 run_cell 重写里做):
- gate_wrapper_command():payload 包一层 sh 起步门,等
  /run/chv/.cell-gate-<machine> 出现再 exec(绝对路径
  /usr/bin/sh:volatile 单元根里 /bin 未必有)。
- find_cell_init_pid() 改带超时轮询(timeout 默认 0.0 = 单次,
  旧调用行为不变;缺目录不再直接回 None;proc_alive 回调让
  nspawn 已死时立刻回,不空等)。
新测试:CellGateTest、CellInitPidPollTest(5 个)。
2026-11-07 10:29:13 +00:00
host 179a86d655 fix(chv-muse-r11fix): browser 快照改 Page.aria_snapshot()(复测新发现 2)
Playwright 1.64.1 的 Page 根本没有 accessibility 属性(那是
Puppeteer 的 API),open/snapshot 在真实库上必报
'Page' object has no attribute 'accessibility'。原生快照是
Page.aria_snapshot()(返回 ARIA 快照字符串),a11y 字段仍叫
a11y,值改为字符串;截断改字符串版 truncate_aria(200 节点
行/64KB)。FakePage 同步改真形状(旧 fake 自带 .accessibility
与真实库分叉,是与 sync_playwright bug 同款模式)。
wheel 1.64.1 实测:sync_api/_generated.py 无 def accessibility,
Page 与 Locator 均有 aria_snapshot() -> str。
2026-11-07 07:02:37 +00:00
host 581e850eb7 fix(chv-muse-r11fix): cell 只遮 /usr 下路径(复测新发现 1)
--volatile=yes 下容器根是基本空的 tmpfs(只挂进只读 /usr),
nspawn 按容器根解析 --inaccessible 路径,/etc/chv 与
/home/chv/.git-credentials 在容器根里根本不存在,解析失败即
Failed to resolve 而起不来;两条 /usr 旗可解析,保留。
凭证文件按构造即不可达(容器根里无 /home、无 /etc/chv),
遮蔽不再是必须项,安全属性不变。
探针证据:celldiag2/3/4——去 inaccessible 后完整 argv 0.4s/rc=0,
单元内 $HOME/.cache 可写,单元内 ls /etc/chv 本来就不存在。
2026-11-07 07:02:37 +00:00
host 04287f6053 fix(chv-muse-r11fix): chv-browser 补 sync_playwright 调用括号 + 接受 broker 的 browser op(复测新发现)
复测(RETEST.md §browser-defect、§broker-proto):
1. _load_playwright 返回裸 sync_playwright 函数(未调用),
_ensure_browser 对它调 __enter__ 即 AttributeError,open
永远失败。改返回 sync_playwright()(调用后的上下文管理器),
与单测假工厂的形状一致。回归测试用 stub 的 playwright 模块
钉住“返回调用结果、不是函数本身”。
2. broker 按设计原样转发 {op: browser, …},chv-browser 白名单
却不认 browser。在 chv-browser 的 handle_request 里归一化:
内层 op 在 action 里;无 action 但带 url 时默认 open(驱动
只发 url);action 同样受 FORBIDDEN_OPS 约束。broker 保持
原样转发不动(其 docstring 与单测均以此为契约)。
附:本提交含 test_chv_muse_p2.py 里 CellArgvTest 的配套更新
(c1d00b3 的 --volatile=yes 断言,同文件无法拆分提交)。
2026-11-07 05:24:42 +00:00
host c1d00b3620 fix(chv-muse-r11fix): cell 改 --volatile=yes(复测纠正缺陷 2)
复测(RETEST.md §cell):--ephemeral 在客内 ext4 根盘(2.4G、
已用 1.8G、可用 593M)上做整盘快照拷贝,空间不够即 ENOSPC,
全部 cell 调用失败(每次约 10.5s veth 超时后 rc=1)。改
--volatile=yes:根为基本空的 tmpfs、/usr 只读挂进,单元内除
bind 出去的目录外一律可写、用完即弃——与 --ephemeral 的设计
意图一致,但不拷盘、不占空间(systemd-nspawn 手册口径)。
--volatile=state 不行:整树只读、仅 /var 为 tmpfs,codex 写
$HOME/.cache 等非 bind 路径会失败。探针已证实 yes/state 均可
启动(0.4s,rc=0),取 yes 保可写性;真机 codex 入 cell 由
第二轮复测验证。测试:CellArgvTest 断言新值、--ephemeral
不再出现。
2026-11-07 05:24:30 +00:00
host 4a7ca25334 fix(chv-muse-r11fix): browser.sock 移进 chv-browser 自有目录(缺陷 4)+ vsock 自动启动
缺陷 4:/run/chv 是 0755 root root,chv-browser 在 bind 时
EACCES 而无限 crash loop(restart 21)。browser.sock 从
/run/chv/browser.sock 移到 /run/chv-browser/browser.sock
(broker 与 browser 两端常量同改);build.sh 建目录 + tmpfiles
登记(0755 chv-browser 所有)。不用组可写 /run/chv——那会给
chv-browser 对 broker.sock/cell.pid 的 unlink 能力。
vsock 自动启动:此前 build.sh 只拷 vsock_notify.py、不建服务,
vsock 通道从未自动启动。按 vsock_notify.py 的 docstring 补
vsock-notify.service(ExecStart=/usr/local/bin/vsock-notify %H,
Restart=no,After/WantedBy=multi-user.target)并 enable;
printf 格式串里 %H 写 %%H。测试:干跑针 /run/chv-browser;
vsock 在场时干跑含服务与 enable 语句。
2026-11-07 04:01:24 +00:00
host f093558c0d fix(chv-muse-r11fix): authd 层 5 去掉 store helper,chv-sync 回落改 fail-closed
round-11 真机测试证实:镜像 /etc/gitconfig 的 [credential]
helper = store 把 authd 释放的长效 token 写进了客内
/home/chv/.git-credentials(0600)。改动:build.sh 不再设
store helper;chv-sync boot 在 authd socket/helper 缺失时直接
die,不再把 token 写进 .git-credentials(旧落盘回落废止);
authd helper 是唯一凭证 helper,且自身绝不落盘。旧镜像上 boot
现在会直接失败,须用含 authd 的新镜像。测试:无 authd 时 boot
失败且 .git-credentials 不产生、干跑不再含 store helper;P3
差距表 §3.5 更新为已关闭。
2026-11-07 04:00:04 +00:00
host 9f593d62ef fix(chv-muse-r11fix): authd 层 3 socket 用 group 权限、层 4 裸 .git 当 pull
层 3:AuthdServer 不再 0700/0600 一刀切。socket 共享组取显式
socket_group 或第一个非 root 放行 uid 的主组(build_server 透
传;生产默认 allowed_uids=(0,1000) 派生 chv 主组),目录 0750
+ socket 0660 + chgrp;解析不出(uid 无 passwd 条目)回落
root-only,绝不放 0666(探针 fixprobe8 用的 0666 不上生产)。
层 4:_operation_for_path 加裸 .git → GET/pull;Sentinel
gitea 规则加 (GET, suffix, .git),POST 裸 .git 仍拒。语义已定:
裸 .git 一律当 pull(只读默认),push 只走显式 git-receive-pack。
测试:Sentinel 允许 GET/拒绝 POST 裸 .git、helper 端到端裸路径
取凭证、组 gid 派生与回落、显式组落地 0750/0660。
2026-11-07 03:58:45 +00:00
host 2d12179b9a fix(chv-muse-r11fix): authd 层 1 叶子树补 errors.py + stub __init__、层 2 seed 补三键
层 1:真实的 cloud/chv/__init__.py import executor/orphans,拷进
authd 最小叶子树(/usr/local/lib/chv-authd)会让 chv-authd 在
import 时 ModuleNotFoundError 而 crash loop——先是 errors.py
缺失,补上后又在 executor 上失败(探针 fixprobe3)。新增
chv-guest/authd_init_stub.py(仅导出 ChvFailure),build.sh 拷
errors.py + stub 作叶子树的 __init__.py,不再拷真实包 __init__;
P3_LIBS_PRESENT 条件加 errors.py;EXTRA_CONTENTS 加 errors.py 与
__init__.py;两 lockfile 脚本的 DECLARED_SOURCES 同款登记。
层 2:_seed_env_entries 把 guest-env 里的
GITEA_URL/GITEA_USER/SYNC_REPO 抄进 authd-seed(build_server
要求四键齐全,只 GITEA_TOKEN 即 ValueError);没有不补、不编
默认值。测试:seed 三键落盘/缺键不补、干跑夹具含新文件、lockfile
合成清单含新两项。
2026-11-07 03:56:24 +00:00
host b41fb67026 fix(chv-muse-r11fix): 执行器以 sudo 调 chv-cell(缺陷 1)
chv-cell 须 root(nspawn + 配网),执行器经 SSH 以 chv 身份调,
此前无 sudo 即 rc=2。改 _guest_host_command 在单元开时前缀 sudo
(探针 p2.log 的手动调用即 sudo chv-cell run;chv 的 NOPASSWD
sudo 来自 seed users 块)。关时原命令不变。
2026-11-07 03:52:34 +00:00
host e02828c590 fix(chv-muse-r11fix): nspawn 255 拒裸 --no-new-privileges 与 --directory=/(缺陷 2)
systemd 255 把裸 --no-new-privileges 当作带必填参数的选项,吞掉
下一个参数后报错(p2.log 实测);--directory=/ 也被拒,须
--ephemeral 或 --volatile。改 --no-new-privileges=yes,
--directory=/ 后加 --ephemeral:根快照 + overlay,单元内除 bind
出去的目录外一律可写、用完即弃,与 P2 共享客机 rootfs 设计一致;
--volatile 会把根置只读,codex 写 $HOME 下非 bind 路径会失败。
测试:CellArgvTest 断言新值、裸值不再出现。
2026-11-07 03:52:34 +00:00
host a6f9597333 round-12: §20 records 717efc4 merge + final suite numbers
Docs only (no code/test change): §20 now records the merge of
round-11 head 717efc4 (V-9 fixes) as 5d5041b with its conflict
resolution, and the final full-suite result on 5d5041b: 2474 tests,
OK (skipped=9). ruff/mypy state noted.
2026-11-07 01:13:05 +00:00
host d324d87529 fix(chv-muse-round11): lockfile DECLARED_SOURCES 补 P3/vsock 六项
717efc4 镜像真机实测:BUILDINFO.contents 含 chv-authd 三件套、
cloud/chv/authd.py、cloud/chv/sentinel.py、vsock_notify,但两
脚本 DECLARED_SOURCES 没有它们——generate 记 6 项 unresolved,
verify 与 check 双双失败(contents 未覆盖)。V-9 的 912edef
没暴露:它的 contents 里根本没有这 6 项。

与 9f929b1 的 P2 三件套同款修法:六项皆仓库内脚本,版本随
repo_commit。合成 BUILDINFO 清单扩到与 717efc4 真实 contents
一致(另补 systemd-container 进 dpkg 假值),反向对照已验:
只撤修复时 LockfileTest 4 测里 2 测失败。

附带教训:经 ssh->wsl.exe 的命令内层退出码一律被吞成 0
(python3 -c 'sys.exit(1)' 回显 RC=0),此前三项验证只看
RC 会把失败判成通过;此后一律以输出文本为准绳(须见
'lockfile 校验通过' 行且无 unresolved 警告)。
2026-11-07 01:08:28 +00:00
host 5d5041bb7c round-12: merge round-11 head 717efc4 (V-9 real-machine fixes)
Round-11 moved on Gitea after round-12 branched (29d3a5a -> 717efc4):
V-9 guest-env fixes must be in any image built from this branch, or
the round-12 image would ship the known defects V-9 found (guest-env
never created when owner=chv:chv; wake reporting ready ~30s before
the runcmd chown lands). Contents, all from 717efc4:
- build.sh: runcmd chowns /etc/chv/guest-env to chv:chv after
  'start chv-authd', before 'chv-sync boot'
- check/generate_lockfile.py: DECLARED_SOURCES gains P2 trio
  (chv-cell/broker/browser -> repo_commit)
- executor._wait_guest_env_usable: with secrets delivered, wait for
  guest-env readable by the SSH user before reporting ready; timeout
  stops the VM and raises guest_env_not_ready; called from _boot_vm
- hostkeys.py: V-9 root-cause comment (owner always root:root)
- test_chv_gitea_sync.py: 5 new tests

Conflict: build.sh runcmd line only. Resolved as union: 717efc4's
runcmd line (with chown) kept; HEAD's P4 trio + P7 chv-memory
copy-in lines (absent on the 717efc4 side) kept. executor.py
merged cleanly.
2026-11-07 01:05:51 +00:00
host 414e245752 round-12: ruff hygiene on migration code (import sort, narrow store except)
- test_chv_muse_round12.py: drop unused socket/time imports, drop
  unnecessary sys.path.insert (P3 pattern needs none), sort method-local
  imports (activity/capabilities, approval_card/channel/sentinel)
- audit_trail.py: single-line method-local imports (I001)
- swarm.py / presentation.py / browser_tasks.py: store-path fail-safe
  except narrowed from bare Exception to (sqlite3.Error, OSError)
  (DB/file failures only); transport spawn/ping/close handlers in
  browser_tasks.py deliberately stay bare Exception (arbitrary
  transport errors must map to failed/False/close_error)
- NOT fixed (pre-existing style, left as-is): UP031 % formatting
  (used throughout the codebase incl. the copied lines), BLE001/
  TRY004 in untouched code, F401 atomic_write + RUF059 swarm
  (P7/P8 pre-existing)
2026-11-07 00:57:05 +00:00
host 717efc4688 feat(chv-muse-round11): 移植 V-9 真机修复(9f929b1/912edef/9fb0f4b)
- lockfile 两脚本 DECLARED_SOURCES 加 P2 三件套 chv-cell/broker/
  browser -> repo_commit(9f929b1)。
- build.sh 99-chv-sync.cfg runcmd 在 chv-sync boot 之前加 chown
  行:guest-env 由 write_files 以 root:root 写(建用户之前),
  须在 final 阶段改归 chv(912edef;本分支 runcmd 多一行
  start chv-authd,chown 插在它之后、boot 之前)。
- executor._wait_guest_env_usable:密钥已投递时等客内 guest-env
  对 SSH 用户可读再报 ready,超时关 VM 抛 guest_env_not_ready
  (9fb0f4b;runcmd chown 比 SSH 就绪晚约 30 秒)。
- hostkeys.py:本分支 _guest_env_entry 本来就写死 owner
  root:root(P3 重构时已无 ssh_user 分支),912edef 的
  hostkeys 改动无内容可移,只补 V-9 根因注释防回退。
- 测试(5 个新测试,8 个运行):owner 恒 root:root、build.sh
  chown 位置、chv 用户下 wake 后 owner 仍 root、等到可读、
  永不可读抛 guest_env_not_ready。

全量 cloud 套件 2019 OK(skipped=8)。
2026-11-07 00:48:25 +00:00
host da8d0c29fd round-12: fix integration fallout (P9 gate x P1, P7 check x P2/P4 fixtures)
- P1 resident tests: P9's inference gate is a precondition of
  _run_with_vm, so _RealExecCase.make_executor now configures a
  default inference origin (CHV_INFERENCE_ORIGIN=https://xgapi.lol
  + CHV_INFERENCE_ALLOW_HOSTS=xgapi.lol) and a public fake resolver
  (same setup as P9's GateTest). No assertion changed; the gate's
  fail-closed behavior on missing origin stays pinned by P9's own
  ask test. 9 resident tests fixed (7 errors + 2 failures).
- P2/P4 dry-run fixtures: P7's build.sh hard check requires
  chv-memory.py in the repo; add it to both fixtures' copy lists.
  2 failures fixed.
- No production code changed in this commit (test setup only).
2026-11-07 00:43:50 +00:00
host d4cef29cdb round-12: migrate P8 registries + P12 audit to AppStateStore
- app_state.py: host_state_db_path() (host-level DB, distinct from
  per-VM state_db_path); kv_get/kv_put/kv_delete on meta (namespaced
  keys; kv_put skips the no-secrets guard by design — registry docs
  hold user prompts, documented); audit_trail table (P12 record
  shape + hash chain, seq explicit); counts()/backup() gain
  audit_trail.
- swarm.py / presentation.py / browser_tasks.py: JSON-file backends
  replaced by KV rows (p8-swarm / p8-ledger / p8-browser-tasks);
  legacy JSON imported once on first load (renamed .migrated);
  corrupt row / unreadable store returns empty (fail-safe, same
  semantics as before; next successful save self-heals).
- audit_trail.py: StoreAuditTrail (same record shape, hash chain,
  filters, export as JSONL version, backed by audit_trail table);
  migrate_jsonl_to_store() moves a chain verbatim (broken chain
  refused, divergent seq refused, re-run idempotent). JSONL class
  kept for reading old files.
- P12 dict contracts now run against real P3/P5 code
  (ApprovalQueue.record -> build_approval_card ->
  card_matches_channel; CapabilityIssuer.issue -> describe ->
  PermissionRegistry.record_grant).
- P6->P3 local protocol test: real PrivsepClient + real worker
  server + real git status; worker git argv carries the real P3
  helper + useHttpPath; no token in argv/env/audit. runuser + HTTP
  pull stay real-machine-only (see gap docs).
- Gap docs updated: P8 migration done, guest-side browser follow-up
  explicitly left for the real-machine track (no task_id in the
  browser protocol yet; lifecycle needs a real VM to verify);
  P6 wiring + P12 migration marked done; production-readiness.md
  gains §20 (round-12 integration).
- Tests: new cloud/tests/test_chv_muse_round12.py, 24/24 OK;
  test_chv_muse_p8.py ledger corrupt test rewritten for the KV
  backend (+ legacy-import test). P7+P8: 95+1 tests OK.
2026-11-07 00:36:25 +00:00
host 466cf7eae7 merge(chv-muse-round12): 合入 P12 审计轨迹与活动视图 (9759be4) 2026-11-07 00:10:34 +00:00
host 77743e734d merge(chv-muse-round12): 合入 P11 用量计量与配额 (60c371d)
- 冲突仅一处:docs/chv-production-readiness.md §15 撞号——
  P11 节顺延为 §19(§15 P2 / §16 P4 / §17 P6 / §18 P7
  保持不动),节内无自引 §15,无内容改动。
- P11 未决冲突原样保留(见 docs/chv-muse-p11-gap.md §5):
  档位门只做准入、USD 路径不动,等待用户裁决,不悄悄统一。
- 测试:test_usage_quota 22/22 OK。
2026-11-07 00:10:32 +00:00
host b55b49b358 feat(chv): 对齐 Muse P10 支付安全——凭证隔离 + 结账检测 + 每次购买人工审批 + 一次性卡号
- 支付凭证隔离:PaymentCredentialVault(只活内存、白名单
  stripe-link/shop-pay、只存不透明 id + 掩码标签、原始卡号
  PAN 拒绝入库、按次释放绑定商家/金额/货币、单次 + TTL)
- 结账页检测:detect_checkout(URL + 无障碍树信号,确定性
  规则、不读原始 DOM、与 P4 detect_form_risk 同输入形状)
- 每次购买人工审批:PaymentGate 经 P5 对话外通道发审批,
  payload 含确切购买详情;完成时先验签、再验详情摘要
  (篡改即拒且审批保持待处理)、最后单次消费答复
- 一次性卡号:OneTimeCard 绑定商家/金额/货币/有效期,单次
  兑现;SimulatedWalletBackend 沙箱钱包(假 PAN 只在后端
  私有内存、charge 只记账不联网、取消即 void)
- 限额:SpendLimits 三档 minor units 比对,限额不可用时
  不编造上限(limits_checked=False)
- 审计:PaymentAudit 只留允许字段,秘密字段名丢弃 + redacted
- 审批通道最小扩展:ApprovalRequest 增加可选 details 字段,
  不传时 payload 与 P5 完全一致
- 真实支付与扣款不做:全部沙箱验证,真实钱包集成待用户
  另行批准;新测试 79 项,全量 cloud 套件 2037 OK

round-12 集成说明:本提交为 acf98f8 的 cherry-pick(只含 P10
自有 diff,基线 25bcd35 已含的 P3/P5 内容未重复合入;
approval_channel.py 仅加可选 details 字段)。
2026-11-07 00:09:55 +00:00
host 90c42f6e1d Merge feat-chv-muse-p9: inference/telemetry restricted channel
- executor.py: P9 _inference_gate inserted before P1 dispatcher
  _run_with_vm; gate call is the first statement, so deny/ask
  fail-closed before any resident resume or cold wake. P9's own
  _run_with_vm (old-body variant) dropped: its body is byte-identical
  to P1 _run_cold (verbatim move), kept once in _run_cold.
- sentinel.py: P9 superset version wins (P5 base + inference entries
  POST /v1/responses, GET /v1/models + comments).
- inference_channel.py / telemetry.py / host.py: P9 as-is.
- Tests: P9 51 + closeup/gaps/optimize/cloud_hypervisor/fix 272 OK.
- mypy on changed files: P9's 5 cross-track errors gone (P5
  capabilities.py now in tree); remaining 4 are 06c3e17 baseline
  legacy (marker_tools.py:142, executor.py:1573/1578), untouched.
2026-11-07 00:09:26 +00:00
host 240949e444 merge(chv-muse-round12): 合入 P8 后台工作 (8d4771b) 2026-11-07 00:03:00 +00:00
host 320cd36921 merge(chv-muse-round12): 合入 P7 状态与记忆分离 (29fa0c8)
冲突按并集解:build.sh 保留 P2+P3+P4 检查/拷入 + P7 的
chv-memory 检查/拷入(P7 的重复 runcmd 行删掉——同一文件
写两次会盖掉 authd 启动行);buildinfo.py 加 chv-memory 一项;
production-readiness.md P7 一节顺延为 §18。P7 测试 46 项全过。
2026-11-07 00:02:54 +00:00
host b0b9f5ed9d merge(chv-muse-round12): 合入 P6 privsep worker + 确定性安全检查层 (81e4eaf)
唯一冲突是文档 §15 撞号:P6 一节顺延为 §17,基线说明更新
(P3 已在树里,接线走真实代码)。chv-sync.sh 自动合入正确:
P6 的 worker 路径与 P3 的 authd 路径(useHttpPath)都在。
P6 测试 43 项全过(skipped=1 是 root 下 uid 否定测试)。
2026-11-07 00:02:22 +00:00
host d1c9a8e647 merge(chv-muse-round12): 合入 P5 Sentinel 纵深 (25bcd35) 2026-11-07 00:01:39 +00:00
host c24cc0ba92 merge(chv-muse-round12): 合入 P4 浏览器完全对齐 (1c8fc3f)
冲突按并集解:build.sh 保留 P3 authd 检查/拷入/runcmd(含
start chv-authd)+ P4 三件套检查/拷入(P4 的重复 runcmd 行
删掉——同一文件写两次会盖掉 authd 启动行)+ P4 的
cdp-broker.service/tmpfiles(含 ask-queue)+ P3 叶子模块条件
拷入;buildinfo.py 两边内容项并集;chv-browser.py 取 P4 重构
版(broker 客户端);production-readiness.md P4 的 §15 顺延为
§16。P2 的 BrowserTest 改走假 CDP broker(断言不变),两路
干跑夹具补齐并集文件集。
2026-11-07 00:01:35 +00:00
chv 29d3a5a474 feat(chv-muse-round11): build.sh 补 P3 authd 进镜像 + gt 脚本哈希轮换
- authd 三件套 copy-in(chv-guest/authd.py 与 git-credential-chv-authd
  -> /usr/local/bin/ 并 chmod +x,chv-authd.service ->
  /etc/systemd/system/),云端叶子模块 cloud/chv/authd.py 与
  sentinel.py + 两个 __init__.py -> /usr/local/lib/chv-authd/,
  systemctl enable chv-authd;99-chv-sync.cfg runcmd 在 chv-sync
  boot 之前加 systemctl start chv-authd;缺件即 fail-fast
- buildinfo BASE_CONTENTS 登记新增件;P2 干跑夹具补 P3 文件并断言
  authd 步骤出现在干跑输出里
- GT_INSTALL_SH_SHA256 cf82... -> 49ffe090...:上游 2026-11-07
  06:44 轮换,V-9 已核实(WSL fresh curl 与失败镜像内残留逐字节
  一致;新脚本全文已读,只从 dl.gitea.com 取 1.0.0 二进制并验 hash)
- vsock_notify 条件拷入在合入后生效(cloud/chv/guest/vsock_notify.py
  已在仓里,干跑不再报缺失警告)
- 定向单测 P1/P2/P3/chv_fix 172 项全过;全量套件仍在跑
2026-11-06 23:08:40 +00:00
host c06ba50983 feat-chv-muse-p9: 推理与遥测受限通道(Sentinel 预审 + 遥测最小化 + 训练退出开关)
- 受限推理通道:cloud/chv/inference_channel.py(origin 解析、
  allowlist、上限、Sentinel 预审),执行器 _run_with_vm 起 VM 前
  必过 _inference_gate(allow 才继续,deny/ask 审计 + fail-closed,
  任务级 extra_env 合并进预审);host.py 第二道防线(_server_command
  的 origin 结构校验 + Server.request 超时/重试走 env)。
  origin 规则:https 公网网关(只 443);http 只接受本地中继
  (字面环回/私网 IP,如 http://172.17.0.1:28084,端口保留,
  无 DNS、无重绑定,allowlist 与单次能力照常);未知 provider
  标签走默认网关(与 _server_command 一致)。
- sentinel.py:从 P5(25bcd35)取的超集版本 + 推理连接器条目
  (POST /v1/responses、GET /v1/models;scheme/端口只 https/443,
  本地中继不走 evaluate,不把 http 加进表)。
- 遥测最小化:cloud/chv/telemetry.py(字段白名单、确定性采样、
  落盘失败不影响任务、token 未回传记 null)+ 训练退出开关
  (默认开,本项目决定;关后轨迹不再产生;损坏 fail-closed;变更审计)。
- 访问边界:docs/chv-access-boundary.md(运营访问口径 + 审计钩子,
  不冒充技术不可访问;Confidential VM 明确未做)。
- 测试:cloud/tests/test_chv_muse_p9.py 51 组全过;5 个旧文件
  的 make_exec 配假 origin + 假解析器(新 fail-closed 门要求的
  测试配置);全量 cloud 套件 1918 OK(skipped=8)。
  详见 docs/chv-muse-p9-gap.md(含自定清单与真机待验证清单)。
2026-11-06 23:03:06 +00:00
host 8d4771bce3 feat(chv): align Muse P8 background work (crons/swarm/presentation/per-task browser)
- crons: pause/resume, exponential-backoff retry (cap 1h), retry claim
  single-winner reusing quota+task build, run_cron_tick cap 5/project
- swarm: per-agent task id + isolated workspace (resolve_inside gate),
  tool register + invocation audit (no execution in swarm layer)
- presentation: worth-presenting rules (needs_input/failed/new
  content only) + notification ledger dedup 1h per (task, reason)
- browser_tasks: one BrowserTransport instance per browser task,
  single-use (reuse after destroy refused), shots dir per instance
- automations: paused status + retry columns (max_retries,
  retry_backoff_seconds, consecutive_failures, next_retry_at)
- gap table docs/chv-muse-p8-gap.md; self-defined values listed there
  (not Meta official): tick cap 5, swarm max 32/default 8, retry
  base 60s cap 3600s, ledger window 3600s, snapshot trunc 200/64KB
- tests: 49 new in cloud/tests/test_chv_muse_p8.py (incl. negatives:
  cron unauthorized trigger, cross-workspace access, no-content
  silence, dedup); full cloud suite 1916 OK (skipped=8)
2026-11-06 22:42:31 +00:00
host 60c371d9d5 feat(chv): 对齐 Muse P11 用量计量与配额执行——四项计量 + 三档档位门 + 哈希链账本
- cloud/usage.py(新):TIER_LIMITS 三档提案(Free $0/Paid $19/Heavy $99,
  提案非定价,payload 带 proposal 标注)、TierStore(无档位行不受档位门
  约束)、UsageLedger(追加写哈希链,篡改可发现)、UsageMeter(token 读
  cost_records、消息读账本、并发/存储注入实测)、QuotaGate(纯准入判定,
  402,不碰在跑任务)、80% 提醒每维度每窗口一次
- cloud/server.py:_reject_if_over_quota 内加档位门(同一漏斗,双人门
  自动覆盖;查询异常 fail-closed 503)、GET /users/me/usage、
  GET/POST /admin/tiers、存储实测复用 _task_worktree_for_files
- docs/chv-muse-p11-gap.md:差距表(先写表再动手)+ 诚实边界(USD 路径
  30 秒轮询掐断与“不掐断”语义不一致是已知待决;并发即拒与设计文档
  排队不同;cloud 任务存储量不到;Heavy 消息/天提案未给,本轨定不限)
- 测试 22/22,全量 1889 OK(skipped=8);mypy 在 usage.py 上干净,
  server.py 5 错均为基线旧错(marker_tools/sub2api/_row_get,与本轨无关)
- 真机验证排队在 round-11 与 P4~P9 之后;不接真实计费
2026-11-06 22:42:30 +00:00
host 29fa0c8813 feat(chv): 对齐 Muse P7——状态与记忆分离
- 每 VM 一个 SQLite 状态库(cloud/chv/app_state.py):
  state_dir/<vm>/app-state.sqlite(0600,WAL),表 tasks/goals/
  sessions/audit(只追加)/meta;写操作过无凭证守卫(键名 +
  值模式);备份用 sqlite3 在线备份 + manifest(sha256),恢复
  先验 manifest、先留 pre-restore 副本;DDL 用 SQLite 与
  Postgres 共通受限子集,换 Postgres = 换连接工厂。
- 记忆文件接线(cloud/chv/memory_files.py):宿主镜像读/列/
  写/追加(路径穿越守卫、写审计);MemorySystemClient 调真实
  记忆系统 CLI(search/get/explain/forget-plan/forget-confirm/
  backup/restore/export),不重造引擎;forget = plan → confirm
  → 本地清行 → search 验证查不到;pull_memory_home 经 SSH +
  tar 拉取客机记忆家目录。
- 客内脚本 chv-guest/chv-memory.py(纯标准库,list/read/
  append/forget/export),家目录 /home/chv/memory(workspace
  之外,不同步到 Gitea);build.sh 拷入 + buildinfo 登记。
- 新测试 cloud/tests/test_chv_muse_p7.py 46 项全过(含真 CLI
  write/search/forget/backup/restore 实测、假 ssh 拉取、否定
  测试);全量 cloud 套件 1913 项 OK(skipped=8)。
- 存储选型:SQLite 不是 Postgres(见差距表 §4;轻量 VM 跑
  Postgres server 太重,宿主共享 Postgres 破坏每用户 VM
  隔离);忘记是尽力删除 + 验证,不是密码学擦除。
- 真机待验证(未做,等测试机空闲):见
  docs/chv-muse-p7-gap.md §6 清单
2026-11-06 22:39:04 +00:00
host 9759be47bb feat(chv): 对齐 Muse P12——审计轨迹与活动视图
统一审计记录模型(时间/主体/动作/对象/结果/原因/关联任务/
关联审批)+ 追加写 JSONL + SHA-256 哈希链(改写/删除/调序可
被 verify 发现)+ 字段级白名单清洗(密钥/token 值/对话原文
绝不进审计);事件分类覆盖任务派发、VM 生命周期、同步、审批、
支付、配额、记忆、后台、推理/遥测;from_legacy_row 承接现有
audit_log 行;导出选 JSON。

活动视图后端:计划登记(已做 vs 计划做,流转受控)+ 已批准
权限登记(接 P5 describe 形状)+ activity_summary 纯函数;
结构化审批卡数据(风险分级 + 接受/拒绝明确动作 + 五档授权
选项,永久档须显式 opt-in)+ card_matches_channel 校验卡与
P5 通道载荷一致。

只做后端 + 数据接口,前端视图留接口;P3/P5 不在本基线,
接线走 dict 契约,集成由 round-11 合并;审计持久化先落独立
JSONL,待 P7 状态库就绪后迁移。真机验证排队在 round-11 之后。

新测试 43/43,全量 cloud 套件 1910 OK(skipped=8);
mypy 在改动文件上干净。
2026-11-06 22:38:19 +00:00
host 1c8fc3f7c8 对齐 Muse P4:浏览器完全对齐(CDP broker 单元外 + a11y-tree-only + 确定性分类器)
- chv-guest/chv-cdp-broker.py(新):单元外 CDP broker,用户 chv-browser,
  独家 chromium.launch(headless=True, args=LAUNCH_ARGS);启动参数白名单
  无 --remote-debugging-port、无 devtools;导航前 URL 校验 + 拦截名单;
  快照恒带 untrusted_input 与 injection_flags;高风险表单在页时 open
  即 ask 入队;观测到导航 POST 自动暂停;命中拦截规则的下载取消 + 暂停
  + ask;全部回包与错误经 sanitize_error + response_leaks 检查。
- chv-guest/chv-browser.py(P2 版重构):改为 broker 客户端,不再直连
  Playwright(本文件无 playwright import),保留 URL 校验 + 快照截断
  作为第二层防线;暂停状态与 broker 同步。
- chv-guest/chv_browser_guard.py(新):纯 stdlib 共享规则——validate_url、
  拦截名单加载/匹配、快照截断、错误消毒(去 HTML、截断 200 字符、凭据形
  key=value 与 sk- 形 token 打码)、注入启发式(中英文各 6 条)、表单风险
  扫描、下载扫描(可执行扩展名)、文件型 ask 队列(pending/allowed/denied,
  与 P3 ApprovalQueue 同语义)、启动参数与回包泄露检查、脚本注入面
  禁止清单(FORBIDDEN_PAGE_ATTRS/FORBIDDEN_OPS)。
- chv-guest/browser-blocklist.txt(新):静态拦截名单;首版只有 RFC 2606
  .test 自测条目,生产条目由运维构建时填入,无在线更新。
- chv-guest/build.sh/buildinfo.py:P2 三脚本 + P4 三文件纳入镜像与
  buildinfo;新增 chv-cdp-broker.service 与 /run/chv/ask-queue tmpfiles。
- docs/chv-muse-p4-gap.md(新):现状 vs Muse 官方要求差距表、改动、
  诚实边界、P2 合入约定、真机待验证清单(round 12)。
- docs/chv-production-readiness.md:补 §15(P4 一节;编号合入时若与
  P2 的 §15 撞,顺延)。
- cloud/tests/test_chv_muse_p4.py(新,36 项):guard 单元、AST 钉住
  禁止属性、假 Playwright broker 测试、假 broker agent 测试、真 Unix
  socket 联调、buildinfo/build 干跑断言;含否定测试(evaluate 被拒、
  DOM 外泄被拒、DevTools 不可达、私有地址被拒、高风险表单被拦、
  注入文本被标注)。

测试:P4 新测试 36/36;全量 cloud 套件 1903 项,两次连续全过
(skipped=8)。附记:第一次全量跑出 1 个失败但输出未保留、后两次
均未复现,疑为时序抖动,未追认。
真机验证未做:排队等 round-11/第三棒释放 100.65.51.33,清单见 gap
表 §5。本地提交,未推未合未部署。
2026-11-06 22:36:56 +00:00
host 81e4eaf4db feat(chv): 对齐 Muse P6 privsep worker + 确定性安全检查层
- cloud/chv/privsep.py(新):Gitea worker 注册表(凭证白名单只含
  GITEA_TOKEN)+ 类型化请求/响应(request_id 绑定、64KB 上限)+
  Unix socket 客户端(超时/崩溃 -> 类型化失败 WorkerRefusal)+
  客户端白名单校验
- cloud/chv/safety.py(新):UntrustedData 来源标记 +
  InjectionScanner 确定性规则(block/flag/allow,中英)+
  redact_secrets + AuditLogger(只记指纹,不记值)+
  audit_inference_request/response
- chv-guest/chv-gitea-worker.py + .service(新):systemd 沙箱化
  worker(SO_PEERCRED 只放 root,仓限 ~/workspace,git 只配 authd
  helper,绝不持 token;输出先扫描再返回)
- chv-guest/chv-sync-worker-client.py(新):agent 侧薄 CLI
- chv-guest/chv-sync.sh:pull/push/status 有 socket 时走 worker,
  否则旧路径 + stderr 警告(过渡兼容)
- cloud/chv/executor.py:_run_guest 前后加推理边界审计钩子
  (best-effort)
- docs/chv-muse-p6-gap.md(新):差距表 + 覆盖边界 + 真机待验证清单
- docs/chv-production-readiness.md:§15
- cloud/tests/test_chv_muse_p6.py(新):43 项

测试:新测试 43/43;全量 cloud 套件 1910 OK(skipped=9);mypy
在新增文件上干净(剩余 4 处为基线旧错 executor.py:1396/1401、
marker_tools.py:142,未动)。
真机验证:未做,排队在 round-11 之后,清单见
docs/chv-muse-p6-gap.md §4。
2026-11-06 22:31:54 +00:00
host 25bcd355f8 feat(chv): 对齐 Muse P5 纵深——eBPF 污点传播 + 五档严格能力 + 对话外审批 + 默认策略
- taint.py:污点追踪器(登记/污点不可逆/子进程继承/审计)
- ebpf_taint.py:eBPF 程序源码 + 加载器(能力探测 + 降级);
  本机实测加载被 seccomp 挡(bpf() EPERM),报 blocked 不冒充
- capabilities.py:五档严格能力(一次/会话/任务/时限/永久,
  三元组绑定 + 精确匹配 + 撤销;permanent 默认不签发)
- policy.py:默认策略规则表 + 谨慎度三档(写操作与敏感路径
  永远不自动放行,规则变化审计)
- approval_channel.py:对话外审批通道(nonce + 15 分钟过期 +
  HMAC,答复一次有效,对话伪造无效)
- sentinel.py 接线:evaluate() 可选接 taint/policy/tier/scope,
  不传时行为与 P3 一致;自动放行步骤 + 档位签发回落单次;
  Capability 基类属性放宽为 Optional(None = 不限)
- 新测试 61 项;全量 cloud 套件 1958 项 OK(skipped=8)

真机待验证(未做,排队在 round-11 之后):见
docs/chv-muse-p5-gap.md §4
2026-11-06 22:25:55 +00:00
host ee456e6625 merge(feat-chv-muse-p3): P5 起点——authd 替身凭证 + Sentinel 最小实现 2026-11-06 22:06:23 +00:00
host 73176e135c feat(chv-muse-round11): 执行器接 nspawn 单元(P2 差距表 §4)
客内 host.py 调用改为 chv-cell run -- python3 /app/host.py
--state /home/chv/.codex/host-state.json --codex-home
/home/chv/.codex --sandbox danger-full-access …,其余原参数
原样保留。--sandbox 放全局参数最后(argparse 后胜出),
单元内恒为 danger-full-access,调用方 read-only 不得盖回
bwrap;非单元路径(本地/Docker/冷启动回落)不动。

开关 CHV_CELL_ENABLED:只认 0/1,默认 1 开;非法值
ValueError(_to_switch 同款口径)。关时走原命令,给
lite10 及更早无单元的旧镜像留逃生口。

测试:新 test_chv_muse_round11_cell.py 7 项(开/关 argv、
原参数保留、sandbox 只在单元路径、非法值报错、SSH 层
接线);test_host_command_options_identical_across_executors
显式关单元测三执行器参数全等,断言本身未动。
全量 cloud 套件 2011 OK(skipped=8)。
2026-11-06 21:58:01 +00:00