feat: improve goal mode and Codex App resilience

This commit is contained in:
shiyue
2026-08-18 21:27:58 +08:00
parent 389690af64
commit 8fe31e0a5e
27 changed files with 631 additions and 24 deletions

View File

@@ -0,0 +1,7 @@
{"file":".trellis/spec/frontend/index.md","reason":"核验前端检查清单和组件边界"}
{"file":".trellis/spec/frontend/component-guidelines.md","reason":"核验标签 DOM、可访问性和普通消息隔离"}
{"file":".trellis/spec/frontend/quality-guidelines.md","reason":"核验缓存版本、前端回归和视觉限制说明"}
{"file":".trellis/spec/backend/index.md","reason":"核验后端会话处理检查清单"}
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"核验标题持久化、广播和回归覆盖"}
{"file":".trellis/spec/guides/cross-layer-thinking-guide.md","reason":"核验 DOM、WebSocket、持久化和会话列表的一致契约"}
{"file":".trellis/tasks/08-18-goal-mode-title/research/goal-message-title-chain.md","reason":"核验实现是否符合已确认链路与并行工作边界"}

View File

@@ -0,0 +1,7 @@
{"file":".trellis/spec/frontend/index.md","reason":"加载前端开发检查清单与组件规范入口"}
{"file":".trellis/spec/frontend/component-guidelines.md","reason":"约束 Goal 标签 DOM、可访问性与样式隔离"}
{"file":".trellis/spec/frontend/quality-guidelines.md","reason":"约束前端静态契约、回归和缓存版本质量"}
{"file":".trellis/spec/backend/index.md","reason":"加载后端开发检查清单与会话处理规范入口"}
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"约束会话持久化、标题保护与回归质量"}
{"file":".trellis/spec/guides/cross-layer-thinking-guide.md","reason":"Goal 改动跨越 DOM、WebSocket 事件、会话持久化和列表广播"}
{"file":".trellis/tasks/08-18-goal-mode-title/research/goal-message-title-chain.md","reason":"提供已验证的 Goal 消息、标题、主题和并行修改边界"}

View File

@@ -0,0 +1,32 @@
# 修复 Goal 模式标记与自动标题
## 问题
设置型 `/goal <目标>` 已经以普通用户气泡持久化和展示,但气泡与普通消息完全一致,用户无法识别这是 Goal。与此同时,`/goal` 由服务端 Slash/RPC 旁路处理,不会先创建模型回合,因此模型没有机会主动调用 `ccweb_set_title`,默认标题会一直保留为 `New Chat` 或 `Untitled`。
## 需求
1. 仅对 `role: user` 且 `ccwebGoalCommand.action === 'set'` 的消息增加 Goal 专用 class,并在正文上方显示固定标签“目标模式”。
2. 实时 `session_message` 与刷新后的历史消息必须走同一渲染入口;普通用户消息、控制型 `/goal` 命令和其他 display-only 消息不得出现该标签。
3. 标签保持紧凑、可读,不把用户气泡重新变成大系统面板;提供基础样式和 `wasteland` 暗金荒野主题适配,并保证长正文和窄屏不溢出。
4. 设置型 `/goal` 持久化展示消息时,仅在当前标题仍为 `New Chat`/`Untitled` 且 `titleSource` 不是 `manual` 时,从 objective 派生标题。
5. 派生标题压平连续空白、截取最多 60 个字符,写入 `session.title` 与 `titleSource: 'derived'`;不得伪造只接受 `source: 'llm'` 的标题历史事件。
6. 标题改变后立即保存,并向查看该会话的客户端发送 `session_renamed`,再广播最新 `session_list`;手动标题和任何非默认标题必须保持不变。
7. 保持 Goal RPC、稳定请求 ID 去重、display-only 持久化、失败草稿语义和控制型 `/goal` 行为不变。
8. 修改 `style.css` 后推进样式 cache-busting 版本,并同步相关回归断言。
## 验收标准
- 回归证明 Goal 消息生成专用 class 与“目标模式”标签,普通用户消息不会误命中。
- 回归证明默认标题会从多行 Goal objective 派生、持久化为 `derived`,并实时发出 `session_renamed`/`session_list`。
- 回归证明手动标题和已有非默认标题不会被 Goal 覆盖。
- 重复同一 Goal 命令仍只产生一条展示消息、一次 Goal RPC,且不会重复改标题。
- `node --check`、相关前端契约测试、`timeout 60s npm run regression` 与 `git diff --check` 通过。
- 在可用的真实浏览器中检查桌面、窄屏和 `wasteland` 主题;若本机仍无浏览器能力,必须如实记录限制,不得以自动化替代视觉结论。
## 非目标
- 不让 `/goal` 额外启动普通模型 turn,也不为了改标题调用内部 HTTP/MCP 回路。
- 不覆盖用户手动标题,不把 Goal 自动标题记作 LLM 标题历史。
- 不重做普通用户气泡、系统消息、其他主题或 welcome 页面。
- 不修改当前工作树中 Codex App 重试任务的文件和区块。

View File

@@ -0,0 +1,22 @@
# Goal 消息与标题链路
## 已确认入口
- `public/app.js`:`buildMsgElement()` 把完整消息对象作为 `meta` 传给 `createMsgElement()`,适合直接读取 `ccwebGoalCommand`,同时覆盖实时和历史消息。
- `server.js`:`persistCodexAppGoalDisplayMessage()` 创建 `role: user`、`ccwebDisplayOnly: true`、带 `ccwebGoalCommand.action: 'set'` 的持久消息,并统一保存会话。
- `server.js`:`handleCodexAppGoalSlashCommand()` 在普通消息运行时之前处理 `/goal`,因此不会触发模型主动调用 `ccweb_set_title`。
- `server.js`:`ensureCodexAppGoalThread()` 复用 `codexAppThreadParams()`;`codexAppThreadConfig()` 会从 runtime MCP 配置写入 `mcp_servers.ccweb`。因此底层 Goal thread 已注册 ccweb MCP,缺口是 `thread/goal/set` 本身不是模型工具调用。
- `server.js`:普通消息只在标题为 `New Chat`/`Untitled` 时取正文前 60 字并设置 `titleSource: 'derived'`。
- `server.js`:`setCurrentConversationTitle()` 展示了 `session_renamed`、`publicTitleMetadata()` 和 `broadcastSessionList()` 的实时通知契约;该 MCP 路径使用 `titleSource: 'llm'`,不能直接复用其来源语义。
- `server.js`:`handleRenameSession()` 将用户标题写为 `titleSource: 'manual'`;`isTitleLockedByUser()` 可阻止自动覆盖。
- `server.js`:`normalizeTitleHistory()` 只保留 `source: 'llm'`,普通 `derived` 标题不进入标题历史。
## 视觉与缓存边界
- 暗金荒野主题 ID 为 `wasteland`。
- Goal 标签只应命中专用 Goal user message selector,不能扩大到普通 `.msg.user`。
- `app.js` 使用动态 `__CC_WEB_FRONTEND_ASSET_VERSION__`;`style.css` 使用固定 query,CSS 变化后需要推进版本并同步回归断言。
## 并行工作边界
当前工作树另有 Codex App 重试修复,涉及 `lib/codex-app-runtime.js`、`scripts/mock-codex-app-server.js`、`scripts/regression.js` 及 Trellis 规范文件。实现可在 `scripts/regression.js` 的 Goal 场景追加断言,但必须保留并发改动,不得重排或还原整文件。

View File

@@ -0,0 +1,32 @@
{
"id": "goal-mode-title",
"name": "goal-mode-title",
"title": "修复 Goal 模式标记与自动标题",
"description": "",
"status": "in_progress",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P2",
"creator": "shiyue",
"assignee": "shiyue",
"createdAt": "2026-08-18",
"completedAt": null,
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": null,
"relatedFiles": [
"public/app.js",
"public/style.css",
"public/index.html",
"server.js",
"scripts/regression.js"
],
"notes": "Goal 用户气泡增加模式标记;默认标题由服务端旁路派生,手动标题受保护。",
"meta": {}
}