fix(role-switch): 角色切换从会话内注入 /cd 改为带 --resume 的进程 respawn#507
Open
xu4wang wants to merge 1 commit into
Open
Conversation
CLI 的系统上下文(CLAUDE.md、记忆路径/索引)是开场按启动 cwd 注入一次
的静态快照,/cd 只改 cwd 不重刷——注入式切换后模型仍拿旧角色的记忆索引
读写:读旧索引(新角色"没记忆")、写新记忆进错桶。记忆注入是 Claude
原生行为,botmux 无独立修复点,只能让"开场"在新 cwd 重新发生。
改法:cd 路由对活 worker 不再注入 /cd,改发带 updateWorkingDir 的
restart——worker 收敛 lastInitConfig.workingDir 后走既有 restart 机制
(杀 CLI + {...lastInitConfig, resume: true} respawn)。--resume 回放
对话历史保留上下文,新 cwd 冷启动让新角色 CLAUDE.md/记忆索引开场注入,
"换角色外壳、留对话内核"。respawn 与 /restart 同机制、适配器无关,
supportsSessionCwdMove 能力位分流随之退场(字段保留,其它路径未动)。
worker 的 restart case 配套三件事:
- restart 合并:已有 restart 在飞(teardown 中/tmux jitter 未触发)时
不叠加第二轮,只收敛 workingDir——叠加会吃掉首轮 teardown、烧重启
预算到 tier-2 强制 FRESH,非 tmux 路径还会双 spawn。
- cwd-move respawn 不计入 consecutiveInWorkerRestarts:用户主动迁移
不是崩溃恢复,计入会让"切角色+一次无关重启"无故强制 FRESH 丢上下文;
respawn 真失败仍有 claude_exit → auto-restart 的计数兜底。
- 复位在飞的 durable turn(对齐 expire_durable_turn),否则会议场景切
角色后消息全卡到 lease 过期、新 CLI 被二次 teardown。
顺带收掉 inject_command.updateWorkingDir 死通道:唯一发送方已不存在,
留着是一个绕过 cd 路由角色库硬校验的免检 cwd 移动口。
⚠️ 部署耦合:本补丁的 respawn 后 resume 探的是新 cwd 桶,旧桶
transcript 必须由 fix(claude) resume 预检跨桶迁移
(rescueOrphanClaudeTranscript)接住。两个修复必须一同部署,
单独上线本补丁 = claude-code 切角色 100% 触发"无法恢复"丢上下文。
已验证:路由/注入/队列策略 48 测全过;真机最坏场景(工具飞行中
kill -9 留悬空 tool_use 尾部)+ 跨桶迁移 + 新目录 --resume 上下文完整。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
--- codex review 加固(2026-07-17)---
- durable 回执:切角色 restart 杀掉在飞的 durable turn 时,主动 emitTurnTerminal(
'ambiguous')——CLI 中途被杀、副作用是否已发无法证明(故非 'cancelled'),交 daemon
重试策略即时对账,取代原先「等 lease 过期 → expire_durable_turn 无法证明 → 扣 ACK →
超时 fencing teardown」的慢路径。复用 emitTurnTerminal 的释放/去重;其 flushPending
微任务因 restartCliProcess 同步置位 cliRestartInProgress 而空跑(已核)。
xu4wang
force-pushed
the
fix/role-switch-respawn
branch
from
July 17, 2026 03:55
c1d0213 to
2f28ea5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
Claude 家族 CLI 的系统上下文(CLAUDE.md、记忆路径/索引)是开场按启动 cwd 注入一次的静态快照,会话内
/cd只改 cwd、不重刷这份注入(CLI 原生行为,botmux 无独立修复点)。当前角色切换走「向活 CLI 注入/cd」(进程不重启),后果是切换后模型仍拿旧角色的记忆索引读写:读旧索引(新角色表现为"没记忆")、按旧路径写记忆进错桶。实测验证:/cd后的系统提示只报 cwd 变更,模型手里的记忆路径仍是开场那份冻结快照(甚至指向已不存在的目录)。修法
「换角色外壳、留对话内核」:cd IPC 路由对活 worker 不再注入
/cd,改发带updateWorkingDir的restart消息;worker 先把lastInitConfig.workingDir收敛到新目录,再走既有 restart 机制(杀 CLI +{...lastInitConfig, resume: true}respawn)。--resume回放对话历史保留上下文;新 cwd 冷启动让新角色的 CLAUDE.md/记忆索引在开场注入。respawn 与/restart同机制、适配器无关,supportsSessionCwdMove能力位分流随之退场(字段保留,其它路径未动)。worker 的
restartcase 配套三件事(对抗式复验驱动):clearTimeout吃掉首轮 teardown、把重启预算无故烧到 tier-2 强制 FRESH(丢上下文),非 tmux 路径还会双 spawn。pending 的 spawn 展开{...lastInitConfig}时自然拿到新目录。consecutiveInWorkerRestarts:用户主动迁移不是崩溃恢复,计入会让「切角色 + 一次无关重启」无故强制 FRESH;respawn 真失败仍有claude_exit→ daemon auto-restart 裸 restart 的计数兜底,回路有界。expire_durable_turn分支):否则会议场景(durable turn 中执行切换)respawn 后durableTurnInFlight残留 true,所有消息卡到 lease 过期、daemon 还会把刚 respawn 的新 CLI 二次 teardown。此修复同时覆盖普通/restart的同族预存在问题。顺带收掉
inject_command.updateWorkingDir死通道:唯一发送方已不存在,留着是一个绕过 cd 路由角色库硬校验(validateRoleLibraryPath)的免检 cwd 移动口。/slash路由测试已断言not.toHaveProperty('updateWorkingDir')把封口写进契约。本 PR 必须晚于或与 #506(xu4wang:fix/resume-cross-bucket) 同时合并/部署。 respawn 后 resume 探的是新 cwd 桶,旧桶 transcript 必须由该 PR 的跨桶迁移(
rescueOrphanClaudeTranscript)接住;单独上线本 PR = claude-code 每次切角色 100% 触发「无法恢复」丢上下文(比现状更糟)。测试
test/ipc-cd-route.test.ts全分支矩阵更新:mode:respawn-resume(restart 携带校验产出的 resolvedPath、repin 先于 send、不 kill、initConfig 同步)、send 抛错回落 cold-restart、无 worker 无条件 killWorker(清残留 pane)、codex/未知 cliId 一视同仁 respawn。相关套件(cd 路由/slash 路由/注入队列策略/slash-inject)48 passed。kill -9(transcript 尾悬空tool_use,模拟 bot 自己在 turn 中发起botmux cd被杀的时序)→ 跨桶迁移 → 新目录--resume完整续回上下文。已知项(不阻塞)
buildArgs不支持 resume 的适配器,respawn 实为全新会话(与旧 cold-restart 行为一致、无回归),但respawn-resume文案对其是过度承诺,后续可按适配器能力降级文案。emitTurnTerminal('ambiguous'),建议另开 issue。🤖 Generated with Claude Code