校准 DirectProject 历史兼容口径的技术方案与共享决策记录
- 技术方案:把「不迁移旧 {role,content} 行 / 不会对旧格式做迁移或兼容」改成当前状态——读侧白名单兼容 legacy 行(含 tool 行只识别不注入)、写侧 DirectProject 自己的旧行写点已收口
- 技术方案:修正「Rust 是该历史文件的唯一写入方」,写清 project.jsonl 同时被通用对话链(agent_id=None 的项目主对话与 Runtime 公开状态消息)读写,并记录两条链互读兼容的规则
- 技术方案:说明为什么故意不给通用对话写入器加「Direct-owned 就拒写」的硬报错(会把旧行噪声换成任务起不来),以及这类失败为何按不可重试处理
- 技术方案:更新时间改 2026-09-11
- decision-log:新增 2026-09-11 条目,记录格式切换当年「唯一写入方 + 测试手动清理」的前提在存量用户项目上不成立、现改为「读侧白名单兼容 + 写侧统一」,以及为什么不做数据迁移
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# DirectProject Codex 原始历史与异常恢复
|
||||
|
||||
更新时间:`2026-09-07`
|
||||
更新时间:`2026-09-11`
|
||||
|
||||
## 目标
|
||||
|
||||
@@ -18,7 +18,9 @@ DirectProject 只使用 `.agent/conversations/project.jsonl` 作为对话历史
|
||||
|
||||
`payload` 必须是未经改写的 Responses item。Direct 回合不由浏览器预写用户 message;Codex 返回的 `rawResponseItem/completed.params.item` 原样追加。显式的本地 user/assistant 补写只能通过受权限保护的 `append_direct_project_conversation_message` 命令完成。native 工具、MCP 工具、reasoning、调用参数和调用结果都保留完整内容,不截断、不摘要、不保存 delta/started 事件。
|
||||
|
||||
DirectProject 不迁移旧 `{role,content}` 行;实现按新格式工作。
|
||||
DirectProject 自己的写侧只写新格式:格式切换(#282)时仍会写旧行的路径已收口——显式 Codex 返回只落在自己的 journal `.agent/conversations/codex-responses.jsonl`,不再投影进 `project.jsonl`。
|
||||
|
||||
读侧白名单兼容旧 `{role,content}` 行:格式切换前,DirectProject 主对话由通用对话写入器落到同一份 `project.jsonl`,存量用户项目的历史文件整份都是这种行。读取时把**明确枚举的那一种**旧行形状(`schemaVersion=game-creator-conversation.v1`、无 `type`、role 在 legacy 写入器自己的角色集合 `user`/`assistant`/`tool` 内、content 为非空字符串)认下来:`user`/`assistant` 投影成与 `direct_project_local_message_item` 同形状的 message item,`role` 与 `content` 逐字节保留;`tool` 行已识别但不进 Codex 上下文(它不是 Responses item,无法还原成真正的工具 item,聊天投影本来也只展示 user/assistant),与 developer/system item 同样过滤。角色集合取的是 `project/conversation.rs` 里那条 `matches!(role, "user" | "assistant" | "tool")` 校验,所以「legacy 写入器能写出的行」被完整覆盖;白名单之外的角色、换了 `schemaVersion`、带 `type`、content 非字符串或为空、缺 `payload`、坏 JSON 仍按损坏失败关闭。这条兼容是只读的,不迁移、不改写历史文件。
|
||||
|
||||
## 正常回合
|
||||
|
||||
@@ -51,8 +53,14 @@ Codex 启动时注入的 `host_skills.instructions`、`permissions.instructions`
|
||||
|
||||
聊天界面只从 message item 提取 user/assistant 内容;工具 item 不再拼成 `tool: ...` 假文本。
|
||||
|
||||
DirectProject 的浏览器层只负责显示和乐观状态,不再调用通用对话写入器;Rust 是该历史文件的唯一写入方。历史读写与回合累计分别位于 `agent/direct_project_history.rs` 和 `agent/direct_project_turn_history.rs`。
|
||||
DirectProject 的浏览器层只负责显示和乐观状态,不再调用通用对话写入器。历史读写与回合累计分别位于 `agent/direct_project_history.rs` 和 `agent/direct_project_turn_history.rs`。
|
||||
|
||||
`project.jsonl` 不是 DirectProject 独享的写入方:通用对话链(`project/conversation.rs`)把「项目主对话」(`agent_id=None`)映射到同一份文件,非 DirectProject 模式(`codex_cli`/`provider`)的项目对话、以及 Agent Runtime 的项目级公开状态消息(`agent/runtime_state.rs` 的 public status 写入点)都由它追加 `game-creator-conversation.v1` 行。两条链的行形状不同但**互读兼容**:DirectProject 侧投影旧行(见上),通用对话侧跳过 `type=response_item` 且带 `payload` 的行(不把它二次投影成自己的记录,DirectProject 侧已经拥有那份投影),其余坏行两侧都失败关闭。因此同一份文件里出现两种行不会让任何一侧失败。
|
||||
|
||||
这里**故意不给通用对话写入器加「文件已属于 DirectProject 就拒绝追加」的硬报错**:这些写入点不是尽力而为的旁路——`agent/runtime_driver/task_start.rs` 在 `ensure_game_creator_agent_runtime_accepted_public_status_at` 返回 `Err` 时会直接中止本次后台任务(「后台任务启动确认落盘失败,任务未执行」),`agent/runtime_protocol/steering.rs` 的三处调用也用 `?` 上抛。加硬报错会把「旧行噪声」换成「任务起不来」,比它要解决的问题更糟;而毒化本身已经不可能发生——legacy 写入器的行形状与角色集合都被 `conversation.rs` 的校验穷举,全部落在 DirectProject 的读侧白名单内。
|
||||
|
||||
## 写入与损坏边界
|
||||
|
||||
写入使用 `write_all + flush`。读取时允许丢弃文件末尾一条不完整 JSON 行;中间坏行直接失败。不会对旧格式做迁移或兼容。
|
||||
写入使用 `write_all + flush`。读取时允许丢弃文件末尾一条不完整 JSON 行;白名单化的旧行投影成 message item;其余中间坏行直接失败。兼容只发生在读取侧,不对旧格式做数据迁移或改写。
|
||||
|
||||
该失败有专门恢复提示,并按不可重试处理:同一份历史文件每次读都会得到同一结论,重试不会改变结果,因此不会向用户显示「可直接重试」。
|
||||
|
||||
Reference in New Issue
Block a user