Files
codex-app-server/docs/thread-resolve-fix.md
T
host 5fd4e23b68 fix: thread/unarchive 走已归档解析(--archived 列表)
probe9 实测:thread/archive 200 后 thread/unarchive 稳定 404。
根因是 #103 时代的逻辑 bug:unarchive 走普通解析,普通
thread-list 不传 --archived 时只回非归档 thread,刚归档的
thread 永远解析不到。修法只改服务端:_list_task_threads 加
archived 模式(传 --archived,home×provider 穷尽不变),
_resolve 与 _pick_latest_thread 加归档开关,_task_thread_mutate
加 resolve_archived 参数只供 unarchive;rename/archive/compact
仍走非归档解析。生产只读验证:同一 worktree 普通列表 0 条、
archived 列表恰好 1 条(probe9 归档的 thread),其 archived
字段为 None。
2026-10-28 06:10:02 +08:00

85 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 线程解析修复:provider 过滤 + 共享 home 回退
## 故障(生产实测 2026-10-28)
本地任务的 thread rename/archive/unarchive 稳定 404
`thread not found`,thread/read 回 200 但 thread 为 null。
而同一任务的 worktree、bindings、快照、心跳、移交全部正常。
## 定位(证据链,全部在生产主机上实测)
1. host-state 里有该任务的 thread_id,cwd 与服务端推导的
worktree 路径一字不差;rollout 文件的 session_meta cwd
也是同一路径;state_5.sqlite 的 threads 表里有该行
(cwd 正确、archived=0、最新)。
2. 不带 provider 注入的 app-server 进程调 thread/list:
全量翻页(cursor 可用)共 77 条,探针 thread 不在其中;
带 cwd 过滤时回空;页内已知 thread 的 cwd 过滤正常回 1 条。
3. 区分点是 provider:列出的 77 条全是 xcmapi,探针两条
是 sub2api;带 `--model-provider sub2api` 注入的进程调
同一 cwd 过滤,立刻回 1 条(正是探针 thread),无过滤
首页即含它。
4. 第二个错位:服务端一直查租户 home
(tenants/<项目>/codex-home),本地 worker 按
host-config-host.json 用共享 home
(/var/lib/codex-app-server/codex-home),旧 thread 全在后者。
结论:app-server 按进程 provider 过滤 thread/list,且
服务端只查租户 home——两个错位叠加,本地任务解析必 404。
不是分页 cap(翻页后仍无)、不是 cwd 记错、不是 home 丢数据。
## 修法(只改服务端,不动派发)
`cloud/server.py`:
- `_run_host_cli` 新增 `model_provider` 参数,拼在子命令
之前(host.py 顶层参数,拼进 argv 会被子命令解析器拒绝)。
- `_list_task_threads` 按序穷尽,首个非空即停:
home:租户 home → 本地 worker 共享 home
(`_local_worker_codex_home`,只读 host-config-host.json
的 codex_home 一键,缺失/解析失败时跳过);
provider:缺省 → sub2api(仅当 server 进程配了
SUB2API_BASE_URL,否则 host.py loud fail,不能变 502)。
任一候选成功回空即停(空就是空);全部报错才 502 首错。
- `_resolve_task_thread` 返回 provider,后续的 thread-read /
rename / archive / unarchive 原样带 provider——同一 thread
在另一 provider 进程里同样看不到,丢了它改操作会 502。
- `_task_thread_read`(#103 的富化 read)此前解析后又用
租户 home 调 thread-read,已改成解析出的 home + provider。
不改派发的原因:本地 worker 改租户 home 会丢共享 home
里的 skills 等内容,有回归风险;且服务端兜底已覆盖全部
现存 thread。租户隔离(本地任务共用一个 home)是另案,
不在本批修。
## 未覆盖
- Docker 任务仍 409(thread 在容器内,服务端查不到是设计)。
- `_task_threads` 列表端点同样走 `_list_task_threads`,兜底
对它同样生效;但它不翻页,只返回解析到的该任务 thread。
- 真 PR 创建仍未实弹(与本批无关)。
- 新测试 9 个:`cloud/tests/test_thread_resolve.py`
(CLI 参数位置、共享 home 读取、回退顺序、空→404、
host.py 失败→502)。
## 后续:unarchive 归档解析
探针 probe9 发现:thread/archive 200 后 thread/unarchive
稳定 404。根因是 #103 时代的逻辑 bug:unarchive 走普通
解析,普通 thread-list 不传 --archived 时只回非归档
thread,刚归档的 thread 永远解析不到。修法(只改服务端):
- `_list_task_threads(task, archived=False)`:archived 为真
时传 `--archived`,只列已归档 thread;home×provider 穷尽
回退不变。生产只读验证:同一 worktree 普通列表 0 条、
archived 列表恰好 1 条(即 probe9 归档的 thread),且其
archived 字段为 None。
- `_resolve_task_thread(task, archived=False)` 与
`_pick_latest_thread(threads, include_archived=False)`:
归档模式下不跳过显式 archived 条目。
- `_task_thread_mutate(..., resolve_archived=False)`:只供
unarchive;rename/archive/compact 仍走非归档解析。
- 新测试 4 个:`cloud/tests/test_thread_unarchive.py`
(--archived 透传、普通解析 404 而归档解析命中、pick
归档开关、unarchive 调 CLI 时解析模式与 provider 正确)。