Agent Runtime 解耦实施进度¶
本文件是实施状态的单一真源。阶段文档定义范围和方案,不维护实时状态。
状态值:not_started、in_progress、blocked、completed。
当前总体状态:completed。
当前工作¶
- 当前阶段:S10 CLI/TUI/ACP consumer 迁移,
completed。 - 当前任务:S10 implementation 与 final gates 已完成;总体 S0-S10
completed。 - 下一门禁:无。
- 当前阻塞:无。
阶段总览¶
| 阶段 | 状态 | 完成度 | 门禁结果 | 方案 |
|---|---|---|---|---|
| P0 基线与护栏 | completed | 7/7 | 通过 | phase-0-baseline.md |
| P1 Streaming core 与事件契约 | completed | 9/9 | 通过 | phase-1-streaming-core.md |
| P2 AgentTurnRuntime | completed | 8/8 | 通过 | phase-2-turn-runtime.md |
| P3 TUI 事件适配与切换 | completed | 9/9 | 通过 | phase-3-tui-cutover.md |
| P4 SessionRuntime 与事件 Broker | completed | 10/10 | 通过 | phase-4-session-runtime.md |
| P5 同项目多会话 | completed | 9/9 | 通过 | phase-5-multi-session.md |
| P6 ProjectRuntime | completed | 11/11 | 通过 | phase-6-project-runtime.md |
| P7 全局控制面 | completed | 10/10 | 通过 | phase-7-global-control-plane.md |
| P8 稳定性与性能收口 | completed | 9/9 | 通过 | phase-8-hardening.md |
| S10 CLI/TUI/ACP consumer 迁移 | completed | implementation、final gates 与 review 完成 | 443 passed;安全全仓 2348 passed、2 skipped(明确排除项见下文) | consumer helper |
P0 任务¶
- [x] P0-01 建立当前 turn/stream/TUI 行为矩阵。
- [x] P0-02 增加代表性 fake-agent 原始流 fixtures。
- [x] P0-03 建立 answer/reasoning/tool/usage 的有序 trace 回归测试。
- [x] P0-04 补齐 cancel/retry/HITL/compact/subagent 边界测试。
- [x] P0-05 建立 runtime 禁止依赖 Textual 的导入护栏测试。
- [x] P0-06 记录可重复的导入、首事件、长输出、取消和内存基线;真实 provider/TUI mounted 指标延后到集成性能阶段。
- [x] P0-07 执行当前 P0 门禁并固化基线报告;完整 fixtures 和性能项仍单独开放。
P1 任务¶
- [x] P1-01 定义
TurnEvent、payload 和版本化契约。 - [x] P1-02 将 ToolItem 等运行领域数据移出 UI 命名空间并保留 re-export。
- [x] P1-03 实现 runtime-owned
TurnAccumulator。 - [x] P1-04 实现
AgentEventSink、collecting/null/composite sink。 - [x] P1-05 实现 legacy
StreamSink与新事件之间的兼容 adapter。 - [x] P1-06 将流归一化和 runner 核心迁入
synapse.runtime。 - [x] P1-07 将 retry notifier 改为并发安全的上下文作用域。
- [x] P1-08 保留旧导入路径并迁移内部调用方。
- [x] P1-09 通过事件契约、兼容和流回归门禁。
P2 任务¶
- [x] P2-01 定义
TurnContext、TurnStatus、TurnResult和TurnHandle。 - [x] P2-02 实现无 Textual 依赖的
AgentTurnRuntime.arun()。 - [x] P2-03 提供受约束的同步兼容入口并处理 sync checkpointer fallback。
- [x] P2-04 将请求构造从
ui/turn/request.py迁入 runtime。 - [x] P2-05 将取消、checkpoint repair、错误和 HITL 终态纳入状态机。
- [x] P2-06 确保所有终态只发一次且结果不依赖订阅者。
- [x] P2-07 添加完整 headless runtime 测试。
- [x] P2-08 通过 P2 API、并发安全和资源清理门禁。
P3 任务¶
- [x] P3-01 定义
TextualTurnEventRenderer的 host 协议。 - [x] P3-02 将标准事件映射到现有 transcript 控制器。
- [x] P3-03 实现 delta 合并、UI 唤醒合并和有界刷新频率。
- [x] P3-04 将 generation 过滤放到 renderer/subscription 边界。
- [x] P3-05 让
TurnController仅调度 runtime 并消费结果。 - [x] P3-06 保持当前 goal/summary/catalog 收尾行为不变。
- [x] P3-07 切换 TUI 调用路径并保留兼容回滚路径。
- [x] P3-08 完成 Textual pilot、顺序和性能回归测试。
- [x] P3-09 通过 Agent loop/TUI 解耦里程碑验收。
P4 任务¶
- [x] P4-01 定义
SessionRuntime状态机和命令接口。 - [x] P4-02 实现有界
SessionEventBroker与 session-local sequence。 - [x] P4-03 实现 snapshot + subscribe(after_sequence) 无缝接续。
- [x] P4-04 将 Agent turn busy/task/cancel/steer 真状态移入 SessionRuntime;UI
_busy保留兼容投影。 - [x] P4-05 将 usage 和 turn accumulator 变为会话状态。
- [x] P4-06 提供冻结上下文驱动的持久化接口,projection failure 不改变turn终态。
- [x] P4-07 将 goal 结算和自动续跑移入 SessionRuntime。
- [x] P4-08 实现无订阅者运行及重新 attach。
- [x] P4-09 实现 event buffer 背压、丢弃和终态保留策略。
- [x] P4-10 通过 detach/reattach、取消和持久化门禁。
P5 任务¶
- [x] P5-01 实现当前项目的
RuntimeManager[thread_id, SessionRuntime]。 - [x] P5-02 为每个运行会话创建独立 Agent graph/SteerQueue。
- [x] P5-03 显式共享 model client、checkpointer 和项目级昂贵资源。
- [x] P5-04 移除 Textual
exclusive=True对 Agent task 的所有权。 - [x] P5-05 实现 per-session run lock 和全局并发限制。
- [x] P5-06 实现会话 attach/detach、后台状态和按会话 cancel/steer。
- [x] P5-07 让会话列表展示 running/waiting/failed/idle 状态。
- [x] P5-08 增加并发串线、切换和 goal 自动续跑测试。
- [x] P5-09 通过同项目多会话门禁。
P6 任务¶
- [x] P6-01 定义
SessionRef(project_id, thread_id)和严格 resolver。 - [x] P6-02 定义惰性
ProjectRuntime与项目资源生命周期。 - [x] P6-03 修正稳定 project identity 和目录移动语义。
- [x] P6-04 将项目
.env改为私有 mapping,停止并发路径修改全局环境。 - [x] P6-05 将 GoalService 从进程单例改为项目实例注入。
- [x] P6-06 将 MCP active pool 改为项目/config digest registry。
- [x] P6-07 隔离 backend、checkpointer、session tools、tool output 和 transcript。
- [x] P6-08 让 RuntimeManager 按
SessionRef路由命令和事件。 - [x] P6-09 实现 idle ProjectRuntime 的安全资源回收。
- [x] P6-10 增加双 workspace 配置、工具和数据库隔离测试。
- [x] P6-11 通过跨项目资源隔离门禁。
P7 任务¶
- [x] P7-01 增加只加载用户层配置的 global bootstrap。
- [x] P7-02 任意目录启动时避免自动注册和污染 cwd。
- [x] P7-03 实现全局 landing page 和项目列表。
- [x] P7-04 实现按项目分组的全局会话列表与搜索。
- [x] P7-05 选择会话时回源验证 workspace、session 和索引新鲜度。
- [x] P7-06 实现跨项目 open/submit/steer/cancel/rename/delete 等操作边界。
- [x] P7-07 显示 missing/inaccessible/stale/running 等状态。
- [x] P7-08 完善 catalog 对账、歧义解析和投影清理。
- [x] P7-09 更新 CLI、TUI、README 和配置文档。
- [x] P7-10 通过任意位置启动和端到端全局操作门禁。
P8 任务¶
- [x] P8-01 建立多会话/多项目长时运行和压力测试。
- [x] P8-02 根据 P0 基线确定并验证内存、事件积压和 UI 延迟预算。
- [x] P8-03 实现 idle SessionRuntime/ProjectRuntime 的 LRU 回收。
- [x] P8-04 验证 running runtime 永不被错误回收。
- [x] P8-05 完成程序退出时 task、MCP、HTTP、SQLite 和 loop 的有序关闭。
- [x] P8-06 增加异常、取消竞争、数据库锁和 provider failure 恢复测试。
- [x] P8-07 完成可观测性、诊断视图和泄漏检查。
- [x] P8-08 运行 Ruff、全量 pytest、MkDocs build 和平台分支检查。
- [x] P8-09 完成最终架构文档、迁移说明和余留风险清单。
阻塞记录¶
| ID | 阶段/任务 | 证据 | 影响 | 解除条件 | 状态 |
|---|---|---|---|---|---|
| - | - | - | - | - | - |
S10 最终收口¶
- TUI C1/C2 最终明确清单:443 passed。
- 安全全仓:2348 passed、2 skipped。明确排除以下 13 个文件,不声称其通过:
test_acp_p0_baseline.py、test_acp_p1_transport.py、test_agent_turn_runtime.py、test_backends.py、test_git_chrome.py、test_herdr_integration.py、test_runtime_service_routing_s5.py、test_runtime_transport_s7.py、test_startup_trace.py、test_transcript_migration.py、test_runtime_daemon_s8.py、test_runtime_transport_client_methods_s9.py、test_runtime_transport_websocket_s7.py。排除原因分为 process API 安全约束及 socket 环境不稳定。 - lifecycle/consumer 核心 194 passed;permit2 5、crossloop 5、generation 4;真实 approval 9;CLI registry 3;project exact/generation tests 通过。
- Ruff、
git diff --check、uv run --no-sync mkdocs build --strict、uv build通过;包版本0.1.43。 - 三轮 review 最终无阻塞发现;workspace freeze Medium 已修复。
- 执行链:
AgentRuntimeService -> RuntimeManager.submit_ref/resume_ref -> SessionRuntime -> AgentTurnRuntime。consumer 不变量:application DTO ports 不暴露 runtime 执行对象;watch detach/connection close 不 cancel turn;close 由同 loop owner 负责并使用 cancel fence;TUI UI-only queue 不拥有 execution runtime;默认 CLI/TUI/ACP 路径不调用stream_agent或agent.ainvoke。 - legacy stream utility 保留兼容,默认 CLI/TUI/ACP 不使用。
- residual/local artifacts note:未跟踪
.sessions.sqlite与transcript.sqlite未删除,不可提交。
验证记录¶
| 时间 | 阶段/任务 | 命令 | 结果 | 备注 |
|---|---|---|---|---|
| 2026-08-07 | P0/P1 针对性回归 | pytest test_runtime_streaming + stream/cancel/tool/textual/turn |
62 passed | 2.80s |
| 2026-08-07 | P0/P1 最终针对性门禁 | pytest runtime/fixtures/stream/cancel/tool/textual/turn/retry |
91 passed | 2.49s |
| 2026-08-07 | P0/P1 最终全量门禁 | pytest -q |
977 passed | 113.15s |
| 2026-08-07 | P2 针对性门禁 | ruff check . + P2/P1/P0 相关 pytest |
104 passed | 3.16s |
| 2026-08-07 | P2 全量门禁 | pytest -q |
987 passed | 99.82s |
| 2026-08-07 | P2 文档门禁 | mkdocs build |
passed | 8.91s;仅仓库既有警告 |
| 2026-08-07 | P3 解耦针对性门禁 | ruff check . + renderer/bridge/TUI/stream pytest |
94 passed | 2.33s |
| 2026-08-07 | P3 全量门禁 | pytest -q |
993 passed | 103.11s |
| 2026-08-07 | P3 文档门禁 | mkdocs build |
passed | 9.14s;仅仓库既有警告 |
| 2026-08-07 | P4 detach/状态所有权门禁 | ruff check . + session/runtime/TUI pytest |
102 passed | 2.31s |
| 2026-08-07 | P4 全量门禁 | pytest -q |
1001 passed | 102.86s |
| 2026-08-07 | retry 并发兼容 | pytest tests/test_model_retry.py -q |
18 passed | 0.90s |
| 2026-08-07 | 全仓 lint | ruff check . |
passed | 无违规 |
| 2026-08-07 | 全量测试 | pytest -q |
966 passed | 103.71s |
| 2026-08-07 | runtime 事件性能 | 10k/50k answer delta 累积 | 0.0175s / 0.0955s | 无 UI、无网络 |
| 2026-08-07 | 文档构建 | mkdocs build |
passed | 9.08s;仅仓库既有警告 |
| 2026-08-07 | P5 针对性门禁 | pytest turn_controller/runtime_manager/dialogs/session_runtime/goals |
140 passed | 37.62s |
| 2026-08-07 | P5 全量门禁 | pytest -q |
1016 passed | 104.36s |
| 2026-08-07 | P5 新增能力 | agent factory 资源共享、切换保持后台运行、会话列表 runtime 状态 | 3 个新测试 | 全量 1016 passed |
| 2026-08-07 | P6 针对性门禁 | pytest project_runtime/config/layered_config/goals/runtime_manager/session_runtime/turn_controller |
102 passed | 5.83s |
| 2026-08-07 | P6 全量门禁 | pytest -q |
1035 passed | 102.78s |
| 2026-08-07 | P6 新增能力 | SessionRef/resolver、ProjectRuntime 惰性生命周期、project.json 身份、私有 env mapping、goal/MCP 注入 | 21 个新测试 | 全量 1035 passed |
| 2026-08-07 | P7 针对性门禁 | pytest project_runtime/cli |
36 passed | 3.28s |
| 2026-08-07 | P7 全量门禁 | pytest -q |
1041 passed | 106.69s |
| 2026-08-07 | P7 新增能力 | load_global_settings、启动解析修正、catalog 对账/resolve/remove、SessionRef 全局路由 | 4 个新测试 | 全量 1041 passed |
| 2026-08-07 | P8 针对性门禁 | pytest test_runtime_hardening.py |
9 passed | 0.51s |
| 2026-08-07 | P8 全量门禁 | pytest -q |
通过 | 见下方最终验证 |
| 2026-08-07 | 最终全量验证 | ruff check . + pytest -q + mkdocs build |
全部通过 | 最终收口 |
| 2026-08-07 | P7 UI 收尾 | 项目侧栏(topbar ≡ 打开)、--session/--project 启动参数、跨项目切换重启 |
新增 drawer + 解析测试 | 全量 1056 passed |
| 2026-08-07 | P7 UI 修复 | drawer 渲染方法名冲突(_render→_paint)、CLI workspace 缺失/判定修复 |
渲染冒烟测试 + 回归测试 | 全量 1058 passed |
| 2026-08-07 | 多会话并发修复 | SessionRuntime 状态回调、ProjectDrawer 实时展示运行 session、session 独立 graph/steer queue、worker 冻结 session 身份、transcript reset 后再 attach renderer、后台结果冻结持久化、退出关闭全部 runtime | 相关 session/TUI/stream/dialog/goal/transcript 测试 283 passed;mounted Textual 生命周期、Ruff 与 MkDocs 通过 | 全量 1114 passed、2 skipped;仅 2 个既有可选原生 tool-output transformer 断言失败 |
| 2026-08-08 | P7 跨项目进程内切换 | drawer 跨项目切换不再退出重启:新增 CodingAgentApp._switch_project(catalog 查 workspace → load_project_settings 快照 → 进程内替换 settings/thread_id/project_root/stores/transcript projection → transcript reset 后 attach);per-project settings/store/transcript projection/goal service 隔离(TurnController.settings_for/projection_for/store_for/goal_service_for,goal 复用全局单例仅当路径匹配);SessionRuntime 增加 project_id;_persist_runtime_result 改为冻结项目资源闭包;修复 _current_project_id 的 catalog/project.json id 分叉误判(同项目 session 误走重启);切换窗口期 busy 回退 _sessions 查询;waiting_approval 纳入后台计数/drawer/前台状态;切回有活跃 runtime 的项目复用冻结 agent |
新增 test_project_switch.py(进程内切换、原项目 runtime 保留、缺失项目容错);更新 2 个旧 exit 断言测试 |
全量 1116 passed、2 skipped;仅 2 个既有可选原生 tool-output transformer 断言失败 |
| 2026-08-08 | 侧栏 UI 精修 | 半宽 26 列(52→26)+ 字符串截断缩短(标题 14/项目名 16);高度避开 topbar 与输入区(height:100% + margin-top:2 + margin-bottom:3,Textual 无 calc);项目图标 📁 + 会话数精简为数字;状态符号+颜色(●/◐/⏸/✕/○);当前会话 ◆ 标记;项目默认折叠最近 5 个会话 + ▸ 展开显示 (N more) 展开全部(记忆展开状态);j/k、Home/End、g/G 导航;_rebuild_tree 统一重建并保留光标;修复无 by_project provider 时全局状态 thread 误合并进每个项目 |
新增折叠/展开测试 2 个;渲染实验(proto)验证尺寸与符号 | 全量 1118 passed、2 skipped;仅 2 个既有可选原生 tool-output transformer 断言失败 |
| 2026-08-08 | 切换断链 bug 修复 | 会话切走再切回后 transcript 冻结(一直 thinking 但内容不渲染):attach() 在 UI 线程执行时 replay broker 历史触发 bridge.emit → call_from_thread 抛 RuntimeError(UI 线程调用非法)→ bridge 被关闭,后续 live 事件全部丢弃。修复:TextualTurnEventBridge._wake 线程感知——UI 线程调用时同步 drain()(安全,已在线程上),其他线程走 call_from_thread;drain 的 reschedule 分支同样复用。三省优化:apply_effects 切换探针改用 runtime_for(不再 attach→detach 导致 replay 脏渲染 + 白建 bridge),渲染 attach 只发生一次(transcript reset 完成后) |
新增回归测试 test_switch_back_keeps_bridge_alive(复现 + 断言 live 事件到达 renderer);3 个 dialogs 切换测试更新为新探针语义 |
全量 1119 passed、2 skipped;仅 2 个既有可选原生 tool-output transformer 断言失败 |
| 2026-08-08 | 切回后 turn 轮换内容丢失修复 | 切回后"turn 开始的内容(思考/工具)没渲染、后续调用才渲染":切走期间 turn 可能结束/轮换,attach 的 replay 事件被 renderer 的 turn_id 门控过滤(绑定新 active turn),旧 turn 事件全部丢弃,restore 投影又可能未包含刚完成的 turn。修复:renderer 新增 replay(event) 路径(不过滤 turn_id,仅 thread_id + sequence 有序),bridge 新增 replay,attach 的 replay 循环走该路径;live 事件仍走 emit(turn_id 门控防跨 turn 污染)。安全性:broker 保留 TURN_COMPLETED(lossless)提供 turn 边界,sink 可正确收尾;实测 debug 确认 replay 的 sink 调用全部执行 |
新增单元测试 test_renderer_replay_bypasses_turn_id_gate(emit 过滤 vs replay 消费) |
全量 1120 passed、2 skipped;仅 2 个既有可选原生 tool-output transformer 断言失败 |
设计变更记录¶
| 时间 | ADR/文档 | 变更 | 原因 | 影响阶段 |
|---|---|---|---|---|
| 2026-08-07 | 初始方案 | 建立总体方案、P0-P8 和进度台账 | 大型重构需可持续跟踪 | 全部 |
| 2026-08-07 | P1 实现 | 采用 runtime accumulator + legacy renderer adapter 渐进迁移 | 保留成熟 stream 状态机行为并先切断结果对 UI buffer 的依赖 | P1-P3 |
| 2026-08-07 | P1 实现 | stream runner/normalizer 迁入 runtime,UI 路径保留 wrapper | 建立单向 runtime -> UI 依赖边界 | P1-P2 |
台账更新流程¶
- 开始工作前:更新“当前工作”,将一个任务置为
in_progress;同一执行者不要并行占用多个依赖任务。 - 代码修改中:发现设计偏差时先更新 ADR 和阶段文档,再继续实现。
- 验证后:将命令和结果追加到“验证记录”,然后勾选任务。
- 阶段完成:核对阶段文档全部验收项,更新总览完成度和门禁结果。
- 阻塞:填写证据和解除条件,将任务/阶段标为
blocked;禁止仅因任务困难而标阻塞。 - 恢复:保留原阻塞记录,追加解除说明,不删除历史。