Conversation
SSK988I
left a comment
There was a problem hiding this comment.
审核基于 bbd02c9。建议修复两个已复现的 P2 问题后再合并:
- 新会话交接后,继续复核或修改时模型上下文缺少交接的计划正文。
- JSONL 尾部截断且没有换行时,取消规划记录会与损坏行拼接,重开会话后恢复操作失效。
验证:在 Windows 上运行 test_plan_lifecycle.py、test_plan_mode.py、test_plan_tui_state.py、test_plan_tool_access.py 和 test_tools_git.py,共 120 passed。另以两个独立用例分别捕获交接后首次模型请求的上下文,以及验证截断日志取消后再次打开的状态;两项均复现失败。具体触发条件、影响及修复建议见对应行内评论。
| reason="handoff", | ||
| related_session_id=source.header.id, | ||
| ) | ||
| target.append_plan_revision( |
There was a problem hiding this comment.
[P2] 将交接 revision 提供给后续复核/修改的模型上下文
这里仅把计划保存为 PlanRevisionEntry,随后 _attach_session_manager 清空消息;build_session_context 只从消息条目重建对话,而有效 system prompt 也没有包含交接 revision。因此新会话虽然在 UI 显示计划,用户选择“继续修改”并发送“复核交接方案并增加回滚步骤”时,模型只收到这句要求,收不到原计划正文。已用带唯一标记的计划交接后捕获首次 stream_fn 的 system_prompt/messages,确认标记不存在。请在子会话继续复核/修改时将该 revision 作为明确的计划上下文提供给模型(保持规划历史隔离和再次确认要求),并增加上下文级回归测试。
| with open(path, "ab", buffering=0) as stream: | ||
| offset = stream.tell() | ||
| try: | ||
| written = stream.write(encoded) |
There was a problem hiding this comment.
[P2] 在损坏且无换行的 JSONL 尾部之后保留新记录边界
append 模式会将 encoded 直接接到文件末尾。如果进程崩溃留下不带换行的半条 JSON(例如末尾为 {"type":),read_entries 能识别 recovery_error,但用户取消规划时,新 CollaborationModeChangeEntry 会拼进同一损坏行。当前内存显示 cancelled,重新打开后该取消记录被整行丢弃,状态又变成 recovery_error;重新规划后的后续条目也可能出现悬空 parentId。已用“ready → 追加无换行截断尾部 → 重开 → cancel_plan_mode → 再重开”复现。请在保留损坏历史的同时确保后续追加记录从独立行开始,并补充无末尾换行的恢复回归测试;现有坏行测试都额外写入了换行,因此未覆盖此情况。
Handoff revisions were persisted outside model messages, leaving review prompts without the source plan. Project the active handed-off revision into planning context without copying source dialogue or authorizing execution. Unterminated JSONL tails swallowed later recovery entries. Append a separator when needed while preserving damaged history and rolling back the separator on write failure. Add regression coverage for attached and reopened handoff review, branch-local context, truncated-tail recovery, valid tail endings, and failed append rollback. Local validation: 1011 Python tests passed (1 skipped), 54 desktop tests passed; Ruff, Pyright, Python builds, desktop typecheck/build, version checks and CLI smoke passed.
变更摘要
将 Plan Mode 从提示词约束和 Shell 白名单升级为运行时强制执行的、可恢复的状态机。只有用户确认最新的
planId + revision + digest后才能执行;普通回复、退出规划或恢复会话都不构成执行授权。本 PR 为
dev → main,仅包含提交bbd02c9,覆盖 Core、CLI/TUI、桌面端、回归测试和文档。关联 Issue
无本仓库关联 Issue:本次依据已确认的 Plan Mode 改进方案实施,不自动关闭其他仓库的 Issue。
目标分支
dev。dev → main,由维护者评审和决定合并时机。main。实现说明
工具边界
plan_access: observe | control | deny分类,未声明的工具默认拒绝;工具注册和执行前检查采用同一分类。git_status、git_log、git_diff、git_show。通过参数数组启动 Git,关闭 pager、提示、optional locks 和 fsmonitor;diff/show 禁用 external diff、textconv 和颜色,log 禁止输出 patch。显式提交与状态重放
submit_plan({title, markdown}):仅 drafting 可用,成功后持久化 revision 并结束规划回合。普通文本、中止或截断的回复不能生成新 revision。recovery_error,禁止执行。执行与交接
started,再注册 live run、启用 Default 工具;结束时持久化终态后通知客户端。started恢复为uncertain,不自动重试。终态写盘失败也释放运行所有权并重放状态。completed,客户端投影为settled,仅表示 Agent 回合正常结束,不宣称任务已验证完成,也不据此生成任务完成记忆。客户端与文档
plan.stateChanged快照;扩展session.snapshot,两端不再自行推进 Plan phase。/plan显示状态菜单,新增--handoff-plan [REVISION],保留旧命令别名。兼容性与限制
<proposed_plan>文本的旧会话显示为候选,必须重新提交后才能执行。测试计划
以下检查已在 Windows 本地完成:
uv run ruff check .uv run pyright --project pyrightconfig.release.jsonuv run pytest -q:998 passed,1 skipped。uv build --all-packagesuv run python scripts/check_versions.pyuv run coding-agent --helppnpm test:54 passed。pnpm typecheck、pnpm build。补充验证:
bbd02c9的 push CI 已通过:Windows、Ubuntu 的 test/build/desktop 共 6 项任务均成功。PR 触发的检查以本页面后续结果为准。提交前检查
dev → main,并核对最新远端基线。CHANGELOG.md更新由维护者在发布流程中确认;本 PR 不更改版本号。CONTRIBUTING.md;按dev → main发布 PR 流程提交评审,不自动合并。