fix(codex): 在工作区会话列表中使用 Codex 的真实会话标题 - #468
Conversation
|
感谢这个 PR 👍 问题定位得很准 —— 侧边栏读的是 DB 持久化行而不是解析器返回值,所以「只改 parser 解决不了侧边栏」这个判断是对的,这也是这类问题最容易踩空的地方。 我把分支拉下来完整跑了一遍验证,全绿:
总体判断:方向合理,parser 那一半做得挺扎实,但建议处理掉下面 2 个问题再合并。 做得好的地方
数据源我也单独核了一遍: 建议合并前处理1. 标题可能写到「换了身份」的那一行上(数据一致性)
同一个缺口还让「并发软删之后仍然写标题」成为可能(UPDATE 里也没重查 建议:把 2. 尽力而为的标题同步不应该能让整个会话列表挂掉
仓库里其实已经把相反的约定写死了 —— 建议:log + 继续,别影响 list 的返回。 建议改(不阻塞,但很划算)3. 写了库但没广播,绕过了两个下游同步
另外 4. 候选查询可以走已有的唯一索引
单看 SQL 延迟这不算大事(实测差 2ms 左右,而且前端有 300ms 去抖)。更值得关注的是首次收敛时那一串逐条 autocommit UPDATE —— 如果一个用户有几百条 index 记录待收敛,那次侧边栏请求会明显卡一下。 5. index 标题 vs rollout 原生
|
|
按照要求又进行了一次修复补充 |
…sion-title # Conflicts: # src-tauri/src/db/service/conversation_service.rs
|
又看了一遍 先放验证结果(在最新 HEAD
上一轮的问题逐条确认
这轮修复引入的两个新问题1. Telegram 网络调用现在跑在列表读路径里
也就是说 好消息是它自限:只有真正改了标题的行才会走,收敛后归零,而且只影响配了 Telegram 桥的用户。但 Telegram 慢或不可达时侧边栏会跟着卡住。 建议:通知是纯副作用,list 的返回值并不依赖它,丢到后台任务即可。 2. 软删文件夹里的会话会被广播进侧边栏候选查询( 于是:软删文件夹里的 codex 会话如果命中了 index 标题,会被刷新 → 广播 upsert → 插进所有客户端的侧边栏,直到下次重新拉取才消失。folder 软删是真实操作( 修复前因为压根不广播,所以不存在这个泄漏,是这轮加广播引入的。加个 folder 存活过滤就好。 一个既有 bug,不该算在这个 PR 头上评审时发现:内嵌桌面 HTTP 的 不过这个在 其余记录一下,都不阻塞
上面两个新问题里,第 2 个(软删文件夹泄漏)我建议合并前顺手带上,改动很小;第 1 个(内联网络 I/O)看你怎么权衡 —— 如果你觉得 Telegram 桥的用户面够窄,也可以先合了再单独优化。剩下的都是记录性质。 整体质量很高,尤其是故障注入和四个 CAS 竞态测试,辛苦了 🙏 |
Follow-up hardening for the Codex session-title sync. Detach chat-channel propagation from the caller. `notify_conversation_title_updates` awaited `sync_conversation_title_to_channels_core` inline, which ends in a Telegram `editForumTopic` POST with a 60s timeout per bound thread. `list_all_conversations` is the sidebar's primary read and is driven per-keystroke behind a 300ms debounce by the search and manage dialogs, so a slow Telegram could hang it. The sidebar upsert still emits inline (DB-only, and the caller's response must not disagree with what other clients were just told); the channel half moves to a detached task. Because a detached edit can land arbitrarily late, `sync_conversation_title_until_current` re-reads the title before every attempt and re-sends if it moved underneath. The loop is uncapped by design: it exits only when the value just read equals the one last sent, so any exit leaves the provider holding the current title, and any fixed cap would exit stale on a long enough run of mid-flight renames. It cannot spin — an iteration happens only when a new title was observed. Serializing per conversation was rejected because the lock would also be taken by the inline rename path, blocking a user's rename behind a stalled background sync. Restrict refresh candidates to what the list can actually show. The candidate query checked only the conversation's own `deleted_at`, while `list_all` also excludes `kind = 'loop'` and conversations in soft-deleted folders. Since every refreshed id is broadcast as an upsert, and the frontend rejects only `parent_id != null`, a refreshed row in a soft-deleted folder was pushed into every client's sidebar until its next refetch. The write-time CAS gained the same folder-alive guard; `kind` stays out of it (written once at insert, never updated). Share the Tauri-managed `ChatChannelManager` with the embedded desktop HTTP server, which built its `AppState` with a fresh one. A fresh manager has an empty channel registry, so every handler renaming a bound thread failed with `NotFound` and never retried. This predates the title-sync work but the new entry points ride the same path. Also give `import_local_conversations_core` the channel propagation the scan and list paths already have. Tests: detachment proven with a semaphore-gated fake backend (upsert already emitted, no edit attempted while parked); convergence proven with a hook that applies a queued rename during each in-flight edit, asserting the provider's last edit equals the row's current title across five consecutive mid-flight renames; candidate scope, the folder-deletion CAS, and the previously untested `title IS NULL` CAS branch covered.
|
直接把上一轮提到的问题在 改了什么1. Telegram 调用挪出列表读路径
2. 后台任务改成「收敛到当前标题」而不是带着快照跑 这是改的过程中 codex review 抓出来的一个我自己没想到的洞:任务如果带着 spawn 时的标题快照跑,而这次 edit 在网络上卡住了,期间用户手动改名成功,那卡住的那次晚一步落地就会把 Telegram 和 所以 没用「按会话加锁串行」是因为那把锁手动改名那条 inline 路径( 3. 候选范围对齐 候选查询加了 4. 内嵌桌面 HTTP 的空 manager(上轮说的那个既有 bug)
测试
两个关键测试我都做了变异验证,确认不是摆设:把收敛循环换成单次发送,第一个失败;把上限还原成 验证
改动本身也过了一轮独立 review(两轮,第二轮就是上面第 2 点那个洞)。 你看下有没有哪里跟你原本的想法冲突,不合适的话随时改回去 👍 |
修改概述
从
$CODEX_HOME/session_index.jsonl读取 Codex 的真实会话标题,不再直接将 rollout 中的第一条用户消息作为会话标题。
修改后,工作区侧边栏、会话详情与 Codex 客户端显示的标题保持一致。
问题原因
Codex 将会话正文和会话标题分别存储在不同位置:
$CODEX_HOME/sessions/$CODEX_HOME/session_index.jsonl原有实现只解析了 rollout 会话正文,因此会将第一条用户消息作为标题。
该标题可能与 Codex 实际生成并显示的会话标题不同。
此外,仅修改解析器并不能解决工作区侧边栏的问题,因为侧边栏读取的是数据库中
持久化的会话记录,而不是直接使用解析器返回的会话摘要。
主要修改
session_index.jsonl中的thread_name。title_locked保护用户手动修改的标题,避免被自动覆盖。updated_at。测试情况
cargo check:通过。-D warnings):通过。