# 真实脚本构建 E2E 调试报告(2026-07-20) ## 1. 结论 本轮已经补齐并实际验证了两个硬门槛: 1. Host 可以通过内部测试专用 Runtime Factory 使用真实 Qwen、真实 AgentRunner、 持久化 TraceStore 和明确的内部 Principal/Auth 运行;非 `test` 环境会 fail-closed。 2. Script Build 的 16 个 Planner、Worker、Validator preset 都有完整、可冻结的模型配置。 第三个硬门槛——“真实旧版输入 → Qwen → Phase 1/2/3 → MySQL 原子发布 → 旧 detail API 回读”——**尚未完整通过**。目前真实链路已经稳定推进到: `真实旧版输入 → Phase 1 → Direction ACCEPT/发布 → Phase 2 → Structure Worker → Structure Validator passed → Structure ACCEPT → Paragraph 规划` Paragraph 之后的 Compose、CandidatePortfolio、Phase 3 Root、Final UoW 和旧 detail 回读还没有在同一条真实运行中全部通过。因此当前不能宣称“真实 E2E 已完成”。 用户明确要求暂不做数据库账号/权限隔离,本轮没有扩大该范围。 > 2026-07-20 推送前补充:最新全量回归发现 Host 有 7 项失败,详见第 6.1 节。 > 这些失败尚未修复;用户随后明确要求停止测试、记录现状并提交推送。因此本报告和 > 对应代码只能作为有已知失败的调试交付,不能视为发布门禁通过。 ## 2. 本轮测试输入和停止状态 - 真实输入来源:上一代 MySQL 数据,而不是 fake fixture。 - 脱敏输入身份:`execution_id=50`、`topic_build_id=230`、`topic_id=400`、 `account_name=创业邦`。 - 模型:通过百炼 OpenAI-compatible endpoint 调用配置的 Qwen 模型。 - 模型 thinking:关闭;请求具有 300 秒超时和 2 次 SDK 重试。 - 本地持久化:`.local/agent-data` 下的 Trace、Task Ledger、Artifact、owner lock 和 E2E report。 - MySQL:经本地 `127.0.0.1:13306` 的受管隧道连接真实输入/测试输出库。 收到停止指令后已执行: - 终止正在运行的 900022 E2E 进程; - 调用 900022 的正式 stop API; - stop API 返回 `stopping`,`stopped_operation_ids=[]`; - 900022 的 stop/fence 意图已经持久化,不允许继续派发新工作; - E2E CLI 写出失败报告 `.local/agent-data/e2e-reports/20260720T081451Z-8233aee0.json`。 这里的 `stopping` 而非 `stopped` 是一个需要继续修的恢复问题:原 Planner 所在进程 先被终止,另一个临时 Host 可以写 stop/fence,但不能等待已经消失的进程内 Planner Task 正常收尾。它不代表模型仍在后台运行;当前没有 `internal_e2e` 进程。 ## 3. 三个硬门槛的当前状态 | 硬门槛 | 当前状态 | 已有证据 | 剩余工作 | |---|---|---|---| | 真实 Runtime Factory + Auth | 已实现,针对性测试通过 | 真实 Qwen tool-call smoke 成功;内部 Runtime/Auth 测试通过;非 test 环境 fail-closed | 最终全量门禁 | | 16 个角色模型配置 | 已实现,针对性测试通过 | manifest 覆盖 16 个 preset,Validator/Worker 温度和迭代上限可冻结 | 最终全量门禁 | | 可执行真实全链路 E2E | 部分通过 | 真实输入、Phase 1、Direction、Phase 2 Structure 已跑通 | Paragraph→Compose→Portfolio→Phase 3→MySQL final→legacy detail | 16 个 preset 为: 1. `script_planner` 2. `script_direction_worker` 3. `script_pattern_retrieval_worker` 4. `script_decode_retrieval_worker` 5. `script_external_retrieval_worker` 6. `script_knowledge_retrieval_worker` 7. `script_retrieval_validator` 8. `script_candidate_validator` 9. `script_structure_worker` 10. `script_paragraph_worker` 11. `script_element_set_worker` 12. `script_candidate_compare_worker` 13. `script_compose_worker` 14. `script_candidate_portfolio_worker` 15. `script_root_worker` 16. `script_root_validator` ## 4. 真实运行中发现的问题、修复和验证 ### 4.1 Runtime 和模型接入问题 #### 问题:Host 缺少可直接执行的真实内部 Runtime Factory 此前生产装配要求外部注入 Runner、PrincipalProvider 和 BuildAuthorizer,但项目内没有 一条可以直接运行真实 Qwen E2E 的内部装配路径。 修复: - 新增内部测试专用 Runtime Factory; - 注入真实 `AgentRunner`、Qwen LLM call 和 `FileSystemTraceStore`; - 注入显式内部 Principal 和 Authorizer; - 仅允许 `environment=test`,其他环境直接拒绝启动; - API Key 只从 Secret settings 读取,不写入报告、Trace 或源码。 验证:内部 Runtime/Auth 针对性测试通过,真实 Qwen tool-call smoke 约 2.01 秒成功。 #### 问题:Qwen thinking、超时和重试不可配置 默认 thinking 会显著增加工具型调用的延迟;SDK 没有项目级请求超时和瞬态失败重试。 900016 的真实运行遇到一次 provider `InternalServerError`,因为当时 `max_retries=0` 直接中断。 修复: - `create_qwen_llm_call` 新增 `enable_thinking`、`request_timeout_seconds`、 `max_retries`; - 默认内部 E2E 关闭 thinking; - SDK 配置 300 秒请求超时和 2 次重试; - 单次调用仍可以显式覆盖 `extra_body`,不会丢失调用方参数。 验证:Qwen Runtime options 单测和真实 tool-call smoke 通过。 #### 问题:工具 schema 无法正确解析 postponed annotation / PEP 604 Union 真实 Runner 注册工具时,部分注解因 `from __future__ import annotations` 仍是字符串, 以及 `A | B` Union 没有完整展开,导致模型看到的工具 schema 不准确。 修复:使用 `get_type_hints` 解析实际类型,并补齐 PEP 604 Union schema。 验证:Agent 工具 schema 新增针对性测试并通过。 ### 4.2 E2E 驱动器问题 #### 问题:轮询会无限容忍服务端 500 早期 E2E poll 把 mission endpoint 的 404/500 都当成“刚启动,继续等”,持久错误会表现 成假卡死。 修复:只允许启动阶段前 5 次 404/500,之后立即暴露原始 HTTP 错误。 验证: - 前 5 次失败、第 6 次成功的测试通过; - 持续 500 在第 6 次直接失败的测试通过。 #### 问题:CLI 只支持 start 新 build 当前 `internal_e2e` 每次都创建新 build,所以每发现一个后段问题,都要重新付出 Phase 1 和 Phase 2 前段的模型时间。 当前只完成了问题确认,尚未修复。详见第 7 节提速方案。 ### 4.3 Phase 1 / Retrieval 状态语义问题 #### 问题:把 `BLOCKED` 子任务误认为终态 900017 中 Retrieval 子任务被 Planner `BLOCK` 后,父 Direction 一直处于 `waiting_children`。Framework 中 `BLOCKED` 是可恢复状态,不会关闭父任务。 修复: - Planner policy 明确:不可用 Retrieval 必须 `CANCEL`,不能用 `BLOCK` 丢弃; - `BLOCK` 只用于 Host 定义的 Root phase boundary; - dispatch 在父任务仍有 blocked child 时返回稳定、可操作的 `TASK_CHILD_BLOCKED`,提示 CANCEL 或 UNBLOCK。 验证:blocked-child 不关闭父任务的针对性测试通过。 ### 4.4 Validator 读不到当前候选的问题 #### 问题:Candidate Validator 只能看到历史 evidence,看不到本 Attempt 的冻结产物 900018 中 Direction/候选 Validator 无法读取正在验证的 artifact,只能反复查询不相关的 evidence,最终既浪费 token 又不能形成可靠 verdict。 修复: - `query_validation_evidence` 返回 validation snapshot 中的 `current_artifacts`; - `current_artifacts` 是当前 Attempt 的精确冻结业务产物; - 历史 Retrieval 仍通过独立的 `current_evidence` 返回; - Validator prompt 要求优先读取 `current_artifacts`,只有存在 raw artifact ref 才读取原图。 验证:候选 Validator 工具面和 frozen artifact 读取测试通过;后续真实 Structure Validator 已经用该路径给出 passed。 ### 4.5 Budget 统计问题 #### 问题:真实 Qwen 上下文超过旧的测试型 token 上限 Runner 会累计多轮 input/output token。原 Task 32k、Phase 2 2m 上限对真实工具型运行过低, 正常验证也会被误判为 budget exhausted。 修复: - Task 上限提高到 1,000,000; - Phase 2 总上限提高到 10,000,000; - Planner 合同要求 `max_tokens=1,000,000`; - 工具 schema 同步合法范围。 这不是要求模型必须消耗这些 token,而是防止 Host 用不现实的测试阈值误杀正常运行。 #### 问题:执行时间把 Planner 等待时间算进 Worker/Validator budget 旧统计按最早 started 到最晚 completed 的墙钟跨度计算,多个阶段之间 Planner 思考和等待 也被计费,导致尚未运行很久的 Task 被判超时。 修复:只累加每个 Attempt/Validation 自己的 `completed_at - started_at` 区间。 验证:跨两小时但实际阶段合计 20 秒的回归测试通过。 ### 4.6 Phase 2 容器生命周期问题 #### 问题:模型提前 dispatch 非执行态容器 900019 中 Planner 创建空 CandidatePortfolio/Compose 后立即 dispatch;这些容器在 adoption closure 为空时本来不可执行。随后模型试图 CANCEL 唯一 Portfolio,破坏 Phase 2 闭合路径。 修复: - Planner policy 增加严格的 7 步生命周期; - 先建 Portfolio 和 Compose 容器,再完成本地候选; - Compose 通过 REVISE 写入精确 accepted decision closure 后才能 dispatch; - Portfolio 在 Compose ACCEPT 后用相同方式闭合; - Host 禁止 CANCEL 唯一 CandidatePortfolio; - 容器不可执行错误改成可操作提示。 验证:唯一 Portfolio 不可取消测试通过;900020/900021 已不再出现提前 dispatch/cancel。 ### 4.7 business scope 与 write scope 混用 #### 问题:模型把业务输入闭包和候选写空间当成同一个 namespace 900020 中 Portfolio scope 没有与 accepted Direction 对齐,Compose/本地候选 scope 也没有 正确嵌套;同时模型把业务 scope 当成 workspace 写路径,导致输入合同和 SQL workspace 分别失败。 修复: - Portfolio `scope_ref` 必须等于 accepted Direction scope; - Compose 和本地创作 scope 必须逐级嵌套; - 内部整稿运行统一使用 `write_scope=["script-build://writes"]`; - prompt 和工具 schema 区分不可变输入兼容性与候选 SQL 写入 namespace。 验证:900021 已成功创建 scope 正确的 Portfolio、Compose 和 Structure。 ### 4.8 Structure Worker 工具使用不收敛 #### 问题:Structure Worker 不知道最短的 workspace 写入顺序 900020 中 Worker 在多个工具之间循环,消息数和 token 快速增长。 修复:Structure prompt 写明精确步骤:读取冻结输入 → 创建各 Paragraph shell → 检查 workspace → 保存唯一 Structure artifact → submit Attempt。Paragraph、Compose 和 Portfolio 也分别补了最短工具路径。 验证:900021 的 Structure Worker 收敛到约 22 条消息、约 33k token,并通过独立 Validator 和 Planner ACCEPT。 ### 4.9 accepted Structure 与 Paragraph scope 不兼容 #### 问题:错误合同直到 Worker 启动才失败 900021 中 Structure scope 为: `.../compose/candidate-1/structure/main` 5 个 Paragraph 被规划为它的兄弟: `.../compose/candidate-1/paragraph/p1` 等。 Paragraph 又把 accepted Structure 作为 immutable input,因此 producer/consumer scope 既不相等也不互相包含,必然触发 `INPUT_SCOPE_MISMATCH`。两个 Worker 已失败,另外三个 也开始重复同一错误,所以该轮被停止。 修复: - Paragraph scope 必须等于或嵌套在 accepted Structure scope,例如 `{structure_scope}/paragraph/p1`; - 在 PLAN、REVISE、SPLIT、DISPATCH 四个边界增加 accepted-input scope guard; - guard 同时检查 ACCEPT action、producer Task、expected task kind 和声明 scope; - 无效合同现在会在启动 Worker 前失败,并给 Planner 可纠正的稳定错误。 验证:新增回归测试证明兄弟 Paragraph scope 在 Worker dispatch 前被拒绝;相关 32 个 Phase 2/Internal E2E 针对性测试全部通过。该修复尚未进行新的真实 Paragraph 运行,因为 用户要求先停止并出报告。 ### 4.10 900022 的状态 900022 使用同一真实输入重新启动,在收到停止指令时仍处于 Phase 1 Retrieval 阶段:Root 从 `needs_replan` 进入 `waiting_children`,3 个 Retrieval 子任务已开始。没有发现新的业务 缺陷;该轮仅因调整测试策略而人工停止。 ## 5. 运行批次记录 | Build | 推进位置 | 停止原因 | 对应动作 | |---|---|---|---| | 900001–900015 | Runtime/E2E bring-up 的早期批次 | 均为人工取消;当前 JSON 报告只有 `CancelledError`,不足以可靠逐条还原 | 不对缺少证据的批次编造错误归因;其问题已按代码修复类别归入第 4 节 | | 900016 | Phase 1 | 百炼/OpenAI-compatible provider 瞬态 5xx,SDK 无重试 | 增加 timeout/retries | | 900017 | Phase 1 Direction 等待 children | Retrieval 被 BLOCK 后父任务永远不闭合 | BLOCK/CANCEL 语义和 dispatch guard | | 900018 | Direction/候选 validation | Validator 看不到当前 frozen artifact;budget 偏小 | `current_artifacts` + budget 修正 | | 900019 | Phase 2 容器创建 | 提前 dispatch 空容器,随后 CANCEL 唯一 Portfolio | 7 步生命周期 + Host guard | | 900020 | Phase 2 Structure | business scope/write scope 混用;Structure Worker 循环 | scope policy + 精确 Worker 工具顺序 | | 900021 | Phase 2 Paragraph | Paragraph 与 accepted Structure 为兄弟 scope | 四边界 accepted-input scope guard | | 900022 | Phase 1 Retrieval | 用户要求停止,切换为局部调试策略 | 已终止进程并写 stop/fence | 所有 E2E JSON 都位于 `.local/agent-data/e2e-reports/`。这些文件被设计为本机运行证据, 不应提交包含环境细节或大体积 Trace 的 `.local` 目录。 ## 6. 已执行测试和当前可信边界 本轮已执行并通过: - Agent Qwen Runtime options + schema annotation:3 passed; - Host internal runtime/E2E/phase contracts/normalization:此前批次 28 passed; - Host phase agent tools/contracts/internal:此前批次 30 passed; - 最新 Phase 2 contracts、agent tools、internal runtime、internal E2E:32 passed; - 最新 scope/blocked-child/unique-portfolio 定向回归:3 passed; - Ruff 对最新 planning/test 修改:通过; - MySQL final publication 定向测试:4 passed、1 skip。该 skip 是用户明确暂缓的数据库 账号隔离,不等于完整 MySQL release gate 通过。 真实运行已经证明: - 能读取完整旧版真实输入; - 能用真实 Qwen 进行工具调用; - Phase 1 能形成并接受 Direction; - Direction publication 和 Phase 2 continuation 可用; - 能创建 Portfolio/Compose/Structure 的细粒度任务树; - Structure 能写候选 workspace、冻结 artifact、由独立 Validator 验证并 ACCEPT。 尚未证明: - 修复后的真实 Paragraph Worker/Validator/ACCEPT; - Compose 对 Structure/Paragraph 的精确 closure 和结构化脚本生成; - CandidatePortfolio ACCEPT 和 Phase 2 checkpoint; - Phase 3 continuation、Root Worker、Root Validator、Root ACCEPT; - 单事务 MySQL final publication 在这条真实运行上的成功; - publication/artifact pointer、branch0 canonical digest 和 legacy detail 的最终一致; - 一条从 start 到旧 detail readback 的完整成功报告; - Agent/Host 全量测试、严格 mypy、compileall、完整 MySQL release gate 的最终一次统一通过。 ### 6.1 推送前最新全量回归(已知失败,未修复) 在拆分本轮提交后、执行 push 前,实际运行了 Agent 和 Host 全量测试。结果为: - Agent:`258 passed in 29.34s`; - Host:`201 passed, 5 skipped, 7 failed in 49.55s`。 7 个 Host 失败分为两类。 第一类共 6 项,均为 scripted real-runner 的 phase boundary 回归: - `test_real_runner_creative_exploration_order_does_not_choose_adoption_order` 的 element-first、paragraph-first、structure-first 三个参数; - `test_mission_service_real_runner_auto_continues_phase_one_to_phase_two_boundary`; - `test_historical_partial_advances_via_http_and_reenters_after_filestore_reload`; - `test_real_runner_phase_one_to_three_final_publication_and_legacy_readback`。 直接表现是 `run_phase_one()` 期望 Root 停在 `PHASE_ONE_CAPABILITY_BOUNDARY`,但 scripted Planner 已继续执行 Phase 2,最终停在 `PHASE_TWO_CANDIDATE_PORTFOLIO_READY`, Host 因此抛出 `ProtocolViolation: mission did not stop at the phase-one boundary`。 已定位根因:本轮把 Phase 2 的完整七步说明及 `PHASE_TWO_CANDIDATE_PORTFOLIO_READY` 常量写进了所有阶段共用的基础 `script_planner.md`。scripted LLM 通过消息中是否出现该常量识别 Phase 2,因此在 Phase 1 就被错误激活。这不只是测试适配问题,也说明 Phase 2 指令没有严格隔离到 Host 的 signed policy continuation,存在真实 Planner 越阶段风险。 计划修复但尚未完成:把 Phase 2 专属说明移出基础 Planner prompt,只在 `build_phase_two_policy()` 生成的 continuation system policy 中注入;同时在 Host 的 Root 规划入口根据受保护 `phase` context 强制 Phase 1 只能创建 Direction、Phase 2 只能创建 CandidatePortfolio。中断前产生的半成品 prompt 拆分已经撤销,没有纳入提交。 第二类共 1 项: - `test_sql_phase_two_dynamic_replacement_compose_portfolio_and_boundary`。 直接表现是 ElementSet replacement 同时引用自己的旧 ElementSet 和已接受 Paragraph 时, 新 `_guard_accepted_input_scopes()` 报 `INPUT_SCOPE_MISMATCH: element-set scope must equal or nest within accepted paragraph scope`。 已定位根因:新增的 planning-time guard 比既有 authoritative `AcceptedInputResolver._validate_consumer_input()` 更严格,遗漏了 ElementSet 将跨 scope Paragraph 用于 placement 的明确合法例外。该 guard 原本用于在 Worker 启动前阻断 900021 的非法 Paragraph→Structure 兄弟 scope,但不能改变既有 ElementSet 组合合同。 计划修复但尚未完成:planning guard 必须复用或完全镜像 authoritative resolver 的 scope compatibility 规则,只为 ElementSet→Paragraph placement 保留既有例外,同时继续 拒绝 Paragraph 以兄弟 Structure 作为 base artifact 的 900021 场景。 按用户明确指示,本节记录完成后不再运行测试。因此: - 上述两类失败仍然存在于当前提交历史; - 没有修复后的测试结果; - 后续接手者必须先修复这 7 项,再继续断点恢复或真实 E2E; - 本次 push 只代表代码和诊断记录已同步,不代表质量门禁通过。 ## 7. 为什么慢,以及后续如何提速 ### 7.1 当前慢点 不是 token 成本,而是调试循环粒度不合理: 1. E2E CLI 每次固定 start 新 build; 2. 每次必须重跑已经验证过的 Phase 1、Retrieval、Direction 和 Structure; 3. 后段第一个真实错误出现后,这一轮前面的时间不能直接复用; 4. 当前 `/resume` 重点覆盖 Phase 3,不能重新启动已有 Phase 2 Planner; 5. `run_phase_two` 一旦检测到已存在 Phase 2 模型输出,会 fail-closed 为 `MissionRecoveryRequired`,这是安全设计,但缺少后续受控 replan 入口; 6. CLI 被 Ctrl-C 后会关闭受管数据库隧道,所以另一个诊断进程需要重新建立隧道。 ### 7.2 已有恢复能力 现有代码不是完全不能恢复: - Ledger、Attempt、Validation、Operation 和 Trace 都是 durable; - Framework 的 `recover_operations` 能把崩溃中状态归一化; - 未真正开始的 pending stage 可以安全 resume; - Worker 已提交而 Validator 未开始时可以只续 Validator; - 已开始但未提交的 Worker/Validator 会 STOP/NEEDS_REPLAN,不会伪装成功; - Phase 3 `/resume` 可以处理 already-complete、finalize-only、Root blocked continuation; - finalize 使用唯一不可变身份,可以重试同一发布而不生成第二份正文。 ### 7.3 当前恢复缺口 - E2E CLI 没有 `--script-build-id`; - Phase 1/2 没有“接管 owner → recover operations → 恢复 Planner trace → 从当前 ledger replan”的完整入口; - stop 后的 build 按 fencing 合同不可解封,这是正确的,因此不能拿已 stop 的 900021 强行续跑; - 新进程对原进程内 Planner Task 的停止收敛仍会停在 `stopping`; - 当前失败报告只记录顶层异常;人工 Ctrl-C 都显示 `CancelledError`,缺少 last checkpoint、 root/task/operation 摘要和最后稳定 DomainError。 ## 8. 后续测试方案 后续不再采用“每修一个小问题就从真实输入 start 一遍”的方式。 ### 阶段 A:无模型快速重放 1. 把 900021 暴露的合同形态做成最小 Ledger/contract fixture; 2. 对 PLAN、REVISE、SPLIT、DISPATCH 逐入口重放; 3. 用真实 Coordinator + FileStore + fake deterministic executor 构造 `Direction ACCEPT → Structure ACCEPT` checkpoint; 4. 直接测试 Paragraph workspace 写入、Validator evidence、Compose closure、Portfolio closure; 5. 对 Phase 3/finalize 使用不可变 accepted artifact fixture 做 MySQL rollback/readback 测试。 这一层应在秒级到分钟级完成,不调用真实模型。 ### 阶段 B:补受控断点恢复 新增内部测试能力,但不放松生产 fencing: 1. E2E CLI 增加 `--script-build-id` 和 `--resume`; 2. 新 Host 先获取同一 build 的 OwnerLease/epoch; 3. 调用 Framework `recover_operations`; 4. pending/未开始阶段只 resume 原 Operation; 5. 已开始未提交阶段归一化为 stopped/needs_replan,再让同一 Planner trace 创建新 Attempt; 6. Worker 已提交时只续 Validator; 7. 复用原 InputSnapshot、accepted Decision、Artifact 和消息父链,不重复 Phase 1; 8. stopped/stopping、未知 child binding、半发布状态继续 fail-closed,绝不直接改数据库状态; 9. E2E report 增加 checkpoint、root 状态、active operation、last DomainError 和 resume 分类。 ### 阶段 C:从中段开始的真实模型测试 1. 先从 `Structure ACCEPT` 测 Paragraph→Compose→Portfolio; 2. 再从 `Portfolio ACCEPT` 测 Phase 3 Root; 3. 再从 `Root ACCEPT` 单独测 finalize→publication→legacy detail; 4. 每段成功后固定为回归测试,失败只重跑该段。 ### 阶段 D:最终只做一次全链路门禁 局部段全部通过后,再新建一个干净 build,执行: `真实旧版输入 → 真实 Qwen → Phase 1 → Phase 2 → Phase 3 → Root ACCEPT → MySQL Final UoW → publication/artifacts published → accepted pointer → branch0 digest → build success → legacy detail readback` 随后统一运行 Agent 全量、Host 全量、MySQL release gate、Ruff、format check、strict mypy、compileall 和 `git diff --check`。只有这些全部通过,第三个硬门槛才标记为完成。 ## 9. 建议执行顺序和时间预估 1. 先补断点恢复/分段 harness 和详细失败报告:约 1–2 小时; 2. 无模型覆盖 Paragraph→Portfolio 的状态和合同:约 30–60 分钟; 3. 真实模型按后半段分段运行并修复:取决于新缺陷数量,预计 1–3 小时; 4. 最终完整 E2E 和全量门禁:约 1–2 小时。 这是调试预估,不是完成承诺。后半段尚未首次真实跑通,任何新的框架级缺陷都会增加时间; 但采用分段和恢复后,不再为同一个后段错误重复支付整个前半程时间。