feat(multi-subagent): record Turn-bound native child reports - #5052
Conversation
huangruiteng
left a comment
There was a problem hiding this comment.
Request changes conclusion (author-owned PR; GitHub blocks formal self-review)
Reviewed exact head: be333f9
Immutable comparison base: 85759ba
本轮发现一个 P2 阻塞:历史增长会让仍然存在的子代理报告从共享状态和 UI 中消失。
动机
#5051 要解决的是“配置了子代理,不代表宿主真的启动过”的可观察性缺口。这个 PR 的有用增量是让主 Agent 的 decision、result、parent review 成为可重读的 Turn 报告,而不是从配置推断执行。它诚实标明 coordinator_reported、host_attested=false;宿主亲自出具证据仍是未交付部分,不能因此关闭 #5051。但这份增量自身需要在后续活动增长后仍能被共享入口读到,目前尚未满足。
改动思路
入口 native-child 先检查已注册 Agent 和 enabled multi_subagent,再由 capability 内的报告转换校验既有 Turn guard。事件复用 rollout journal 和独占文件锁;operation identity 负责幂等,记录不启动子代理、不消耗 quota、不授予 peer 或 delegation 的持久权限。CLI 精确读回、agent-context、status 与详情卡片共享报告数据,不另建任务库。
与原来的 native_host_capacity 即时观察相比,新增状态是无法从配置推导的主 Agent 报告事实,而统计、可见列表和重试说明是派生投影。bound delegation 仍有自己的 runtime/authority 生命周期,不能把这两类记录合并为“已执行”。这个所有权分离合理;问题出在共享 status 用诊断窗口发现事实,而不是在记录端。
具体改动
实际 base..head 为 17 个文件,+994/-8:生产路径 +566/-7,测试/示例 +340/-1,文档 +88。新增 CLI 和报告 reducer 是主要机制成本,五个前端文件只沿现有 Goal 详情路径接入,不增加配置入口。生命周期测试和双语文档与同一个用户结果相关,不是新的调度器。
关键代码讲解
- handle_native_child_command:30:Goal 内注册身份和启用策略先于 record/read;read 不接受写入参数,record 默认 preview。未知 Agent、关闭能力和未绑定 Turn 都有明确拒绝,拒绝不增加事件。
- record_native_child:214:decision 必须有 runnable guard;result 需要 started decision,accepted review 需要 completed result 和证据引用。两次并发提交同一 operation,实测仅追加一次,第二次读回同一记录;冲突不会覆写旧事实。
- native_child_activity:74:先对完整 Turn 聚合,再限制为 8 条可见 operation。我记录 10 个 operation 后仍读到 1 次启动、9 次跳过、1 次验收,说明这个展示上限本身没有截断统计。
- build_attention_queue:1193:这里先在最多 500 条近期事件中寻找 native event,找到才补读完整 Turn。最后一条 native event 也落在窗口外时,补读分支根本不会执行;下游卡片因此没有数据。
对主干的风险
[P2] 不要用近期 Todo 事件窗口判断最新子代理报告是否存在
复现是在独立合成 registry/runtime 上经过真实 CLI,不使用 mock queue:启用 max_children=6,依次 record 一个 spawn→completed→accepted 和九个 skip,再调用 status,能看到上述完整统计;只追加 501 条无关 evidence_log_read,第二次 status 的 project_asset.native_child_activity 就变成缺失。同一时刻 native-child read 对原 Turn 仍返回 coordinator_reported、launched_count=1、skipped_count=9、parent_accepted_count=1。两次 status 的合成契约健康出口均为 1,但都正常返回同一个 Goal 的 attention item;不是命令失败导致未生成投影。记录、Goal 启用状态与身份没有改变,改变的只是无关事件数量。
这违反“latest reported Turn”及持久读回合同,长时运行会把已有事实伪装成没有报告。当前测试只让 snapshot 留下最后一个 native result,所以覆盖了“decision 被截断”,没有覆盖“所有 native event 都被截断”。最小修复是保持 Todo 诊断窗口独立,在 canonical journal 的 native 事件范围内查找最新 Turn,或复用有完整性语义的现有投影;不要扩大所有 Todo 的 500 条预算,也不要新增手工同步的 latest 标志。加真实 status 的 501 条噪声回归,并测日志增长成本。
已运行:32 项 Python 测试、13 项 TS 测试、control-plane typecheck、changed-file Ruff、diff check、status Markdown smoke、dashboard/chat build、桌面与移动活动卡片 browser smoke、packaged personal-workspace smoke,以及上述 CLI 生命周期/边界/并发探针。容量拒绝后同 Turn 重试被挡,父级 skip 仍可记录,新 admitted Turn 可恢复启动;关闭能力的 before_plan 与 base 完全相同。
完整首页 browser smoke 在 base/head 均失败,规范化失败身份与详情同为“desktop Files projection lost its public-safe summary or Goal lineage: Files & Outputs / 0 / 团队报告”。失败发生在进入新增详情卡片之前,Files 投影代码不在此 diff,新卡片独立桌面/移动验证通过。因此它是已有无关失败,不构成本轮拒绝理由。远程 CI 未查询、未等待。
语义与 CI 对齐
本 PR 扩展的是可选报告词汇,不是宿主执行、计费或 actor 生命周期授权。缺失宿主证据继续是 unknown,配置上限不是实时容量;guidance 负责建议报告,record 的阶段/幂等约束则是实际执行的输入合同。必须修复的是共享状态对 canonical 报告存在性的误判,而不是解除安全门禁或降低测试预算。
我的整体评价
报告机制和事实来源区分值得保留;小而相关的下一步重构应落在最新 native Turn 的读模型边界,让存在性发现和精确聚合拥有一致的完整性依据,不要再给大 status 模块堆一个猜测标志。旧即时 capacity 观察仍被原生宿主调用,保留兼容有真实消费者依据;不能把它冒充持久宿主证明。
长期推进和用户体验的结论都是“尚有回归风险”:单次写入能用,但活动增长后的读回承诺失效。修复上述窗口问题、补独立 oracle 回归并重新验证 exact head 后可复审;宿主 attestation/adapter 仍属于 #5051 的明确后续范围。本次不合并、不以 CI 红灯或测试数量决定结论。
English verdict: REQUEST_CHANGES
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
be333f9 to
e4913a9
Compare
…nstant The bare `SCHEMA_VERSION` name joined the cross-module `SCHEMA_VERSION` conflicting-values group and pushed the semantic inventory ratchet from 55 to 56, so `examples/semantic-vocabulary-drift-smoke.py` failed closed with "inventory conflicting_definitions grew to 56; budget is 55 (unreviewed)". Budgets only decrease and a raise needs maintainer approval, so this repairs the cause instead of the number: the sibling module in the same package already uses a qualified constant (`CODEX_HOST_CAPACITY_SCHEMA_VERSION`), and this follows that convention so the new carrier no longer reads as another ambiguous `SCHEMA_VERSION` definition. No behaviour change: the emitted schema value stays `native_subagent_activity_v0`. Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Refine, verification and merge handoffHead: Refine (load-bearing). What was repaired. The head was two days old and 216 commits behind, so most of its red CI no longer described this diff. After the rebase: the Verification at this head. Known boundary (declared by the author, unchanged). Every row is Coverage gap worth naming. The broad Merge authority. This changes |
Problem and behavior
A Goal's
max_childrenand model preference did not show whether a host-native child was attempted, skipped, rejected for capacity, or adopted by the parent. This PR adds a provider-neutral, Turn-boundnative-child record/readcontract. It stores typed decision, result, and parent-review reports with a stable operation ID; duplicate reports are idempotent, conflicts are rejected, and capacity/host failures stop same-Turn spawn retries while parent work can continue.The shared read model keeps the configured ceiling separate from reported starts. Missing reports remain
unknown. Coordinator reports are explicitlycoordinator_reportedandhost_attested=false; they do not become authoritative host observations. Boundloopx delegationkeeps its existing authority.User entry points
agent-contextcan record or read the exact Turn's report. Goal status JSON and Markdown use the same projection.multi_subagentand a reported native decision. The existing configuration editor remains the configuration owner. Desktop/mobile and Chinese/English packaging were checked.Validation
smoke:home-browserrun stopped at its Files projection assertion before reaching this card's assertion. The focused native-child browser path passed; the broad failure's cause is not established here.Remaining boundary
This implements the generic reporting and readback substrate for #5051. LoopX cannot attest an arbitrary host-native tool call without a host integration. This PR does not add a Codex-specific adapter or claim host-observed receipts; keep #5051 open for adapter-backed attestation and end-to-end acceptance.