Files
codex-app-server/docs/chv-muse-p8-gap.md
T
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

125 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 对齐 Muse P8 差距表:后台工作(crons / 子代理群 / 回访通知 / 每任务浏览器子代理)
> 先写表再动手。口径分层:= Meta 安全文(security.muse.ai 2026-09-08)与产品
> 设计文(introducing.muse.ai)原文;= 媒体/评测转述;=
> 二进制拆解与用户 VM 观察。凡是 Meta 未公开的数值(cron 调度粒度、数量上限、失败重试
> 策略),一律写清是**本项目自定**,不得写成 Meta 官方值。
## 1. Muse 官方要求(出处:研究报告 §2.5)
| # | 要求 | 来源 |
|---|---|---|
| B1 | 关应用后继续工作;按计划与相关事件推进 | 设计文 |
| B2 | 完成后台工作后评估是否“值得呈现”,只在真正有新内容或需要输入时通知用户 | 设计文 |
| B3 | 启动成群的子代理、构建自己的工具、编辑自身;VM 需要足够存储、CPU 与内存来做真正的工作——如编译代理写的代码、开发自定义 Skills、处理并发子代理与 crons | 安全文 |
| B4 | 每个浏览器任务由专门浏览器子代理完成 | 安全文 |
| B5 | 定时任务(crons)与并发子代理并存 | 安全文(只提存在) |
| B6 | cron 的具体调度粒度、数量上限、失败重试策略 | Meta **未公开**——本项目自定,见 §4 |
**不在本轨官方口径内的东西:** “目标工作器(objective workers)”一词只见于第三方
VM 观察,未见于官方页面原文,本轨不把它当官方术语用;长期目标的分派与通知步骤
用本轨的 swarm + presentation 实现,不挂这个名头。
## 2. 现状(基线 06c3e17,实测代码为准)
- **crons,两套现成的、都不完整:**
- `host.py run-due`:host state JSON(name/task/repo/interval/last_run),
`now - last_run >= interval` 到期即派发 host 任务,跑完才回写 last_run。
无重试、无失败计数、无启用/停用(只有删定义)。
- `cloud/automations.py`:控制面 automations 表(SQLite/Postgres),
`claim_due_automation` 条件 UPDATE 抢单(同一间隔只一个执行器拿到),
配额门、`automation_runs` 产出记录、分诊收件箱。无重试策略、无 paused
状态、无单项目上限。`MIN_INTERVAL_SECONDS=60`、`MAX_INTERVAL_SECONDS=30d` 已有。
- **子代理群:没有。** 只有模型层的子代理名(`cloud/harness2.py`
`subagent_names`,prompt 树里的名字),没有执行层的并行派发、没有隔离、
没有工具注册。
- **后台回访通知:没有“值得呈现”评估。** `cloud/push.py dispatch_event`
按订阅派发终态(completed/failed/goal-unmet 等);自动化模块 docstring
已写明“不新写通知代码”。无去重、无节流——同一终态可重复派发。
- **每任务浏览器子代理:没有。** P2 的 `chv-browser.py` 是常驻进程,
`close` 只关当前页、进程继续跑、等下一次 open;broker 固定转发到
`/run/chv/browser.sock`。状态跨任务复用。
## 3. 改什么(本轨四个新模块 + 一处扩展)
| 模块 | 内容 | 归属 |
|---|---|---|
| `cloud/chv/crons.py`(新) | 在 `cloud/automations.py` 之上加:启用/停用(paused 状态)、失败重试(指数退避 + 上限)、单项目并发上限与定义上限、tick 驱动(到期 + 重试到期统一认领) | 本轨 |
| `cloud/chv/swarm.py`(新) | 子代理群:建群/派代理/生命周期;每代理独立任务 id + 独立工作区(`resolve_inside` 门,越界拒绝);工具注册与审计(只登记与审计调用意图,执行在代理自己的 VM 里走执行器);上限可配 | 本轨 |
| `cloud/chv/presentation.py`(新) | “值得呈现”评估(需要输入/失败需决策/有实质新内容才通知,常规完成只记日志)+ 通知账本(每任务每原因节流去重) | 本轨 |
| `cloud/chv/browser_tasks.py`(新) | 每浏览器任务一个实例:建/绑/毁生命周期、单实例单任务(复用即错、毁后复用即错)、每实例独立状态目录;transport 接口注入(测试用 fake) | 本轨 |
| `cloud/automations.py`(改) | 只加重试/停用所需的列与抢单入口(`max_retries`、`retry_backoff_seconds`、`consecutive_failures`、`next_retry_at`、`paused` 状态);host.py run-due **不动**(host 侧唯一执行器,见该模块 docstring) | 本轨 |
**P7 状态落点(更新 2026-11-07 06:39;迁移已完成,round-12 更新):**
P7 已完成并提交(分支 `feat-chv-muse-p7`,提交 `29fa0c8`:每 VM
一个 SQLite 状态库 AppStateStore,0600 WAL,
tasks/goals/sessions/只追加 audit/meta)。**round-12 已把三处登记
迁到 AppStateStore**(分支 `feat-chv-muse-round12`):宿主级库
`state_dir/app-state.sqlite`(`host_state_db_path`,与 per-VM 库
同库不同文件),命名空间 `p8-swarm` / `p8-ledger` /
`p8-browser-tasks`,一行存整份登记文档;旧 JSON 文件首次加载时
自动导入并改名 `.migrated`;KV 行损坏/库打不开时回空结构
(fail-safe,下次成功保存即覆盖自愈)。测试钉在
`cloud/tests/test_chv_muse_round12.py`(P8MigrationTest)与
`test_chv_muse_p8.py`(损坏行恢复 + 旧文件导入)。crons 的状态
仍在 `automations` 表,未动。
**客机侧跟进(本轨只写规约、不改 P2 的文件):** 每任务浏览器实例的客机
进程拉起/销毁在 P2 的 `chv-guest/chv-broker.py`(`browser` 操作固定转发到
单例 socket)里改:每浏览器任务起一个全新的 `chv-browser.py`(每任务
socket),`close` 后杀掉进程。P2 的文件不在本分支基线里,本轨不复制、不
分叉,规约写进本表 §6,留给 round-12 集成时落在 P2 分支上。
**客机侧跟进(round-12 决定:未做,留给真机轨):** round-12 核实后未做,
原因三条:① `chv-browser.py` 的请求协议里没有任务 id 字段(只有
open/snapshot/screenshot/close/ping),broker 分不清请求属于哪个浏览器任
务,改 broker 之前须先改协议 + 调 broker 的调用方;② 进程拉起/销毁涉
及以 chv-browser 身份起进程、socket 就绪等待、close 后杀进程与僵尸回收、
broker 重启后的残留清理,在没有真机验证的情况下盲改,坏了就是每任务浏览
器全断;③ 本地只能用假浏览器进程测生命周期,真 Playwright + 真
chromium-headless-shell 的起停只能在真机上验。留给真机轨:先在真机上改
协议(请求加 task_id)→ broker 按任务拉起/杀进程 → 连续两任务端到端
(§6 第 4 项)。**不许写成已做。**
## 4. 自定数值清单(全部是本项目自定,非 Meta 官方值)
| 数值 | 本项目自定值 | 说明 |
|---|---|---|
| cron 最小间隔 | 60 秒(沿用现有 `MIN_INTERVAL_SECONDS`) | 已有,不新定 |
| cron 最大间隔 | 30 天(沿用现有 `MAX_INTERVAL_SECONDS`) | 已有,不新定 |
| 失败重试默认次数 | 3 次 | 超过即停,不再重试 |
| 重试退避 | 基数 60 秒 × 2^(n-1),上限 3600 秒 | n 为第几次重试 |
| 单项目并发 cron 执行 | 5 | tick 单轮最多认领 5 个 |
| 单项目自动化定义上限 | 100 | 超过建定义即 ValueError |
| 每群子代理上限 | 8 | 建群时可配,1–32 |
| 每项目群上限 | 10 | 超过建群即 ValueError |
| 通知节流窗口 | 同一任务同一原因 3600 秒内只通知一次 | 账本持久化 |
| 工具名长度上限 | 100 字符 | 与自动化名上限同款 |
## 5. 诚实边界
- B3 的“VM 需要足够资源”是容量要求,不是本轨能代码保证的:本轨只做上限与
隔离,宿主容量仍由部署方保障;群上限 8 是防炸宿主的默认值,不是性能承诺。
- swarm 层**不执行工具**:工具调用意图只登记与审计,真正执行在子代理自己的
VM/单元里(P1 常驻池 + P2 单元是底座)。“子代理构建自己的工具”在本轨指
注册进群内工具表、可被审计、可被别的代理复用调用名——不是沙箱外任意执行。
- “实质新内容”的判定是确定性规则(新产物/新改动文件/显式 notify 标志),
不是模型判断;模型觉得重要但规则判无新内容时,以规则为准、只记日志。
- cron 重试只针对 failed/interrupted 的执行;超配额跳过(skipped_quota)
不是失败,不进重试(沿用 automations 模块现有口径)。
- 通知账本先走原子 JSON 文件,P7 存储(`feat-chv-muse-p7` 提交
`29fa0c8` 的 AppStateStore)已落地,集成轨再迁移;迁移前账本丢了只
影响去重(可能重复通知一次),不影响任务执行本身。
## 6. 真机待验证清单(round-11 与 P4/P5/P6 之后,等通知)
1. cron 端到端:定义在测试机上按间隔触发、失败重试 3 次后停、停用后不再触发。
2. 群端到端:3 个子代理在各自 VM 里并行跑、互不可见对方工作区。
3. 回访通知端到端:后台任务完成后有新内容才通知、无新内容不通知、同一原因
1 小时内不重复通知。
4. 每任务浏览器实例端到端:连续两个浏览器任务各起全新实例、第二个看不到
第一个的截图与快照状态(需 §3 客机侧跟进先落地;round-12 未做,见 §3)。
5. 账本迁移:**已完成(round-12)**——通知账本与另两处登记已迁到宿主级
AppStateStore(见 §3 更新),去重语义不变,测试钉住;真机上只需确认
升级后旧 JSON 文件一次导入、不丢去重状态。