Skip to content

fix(worker-pool): 模型不再冻结进会话,每次启动跟随 bot 配置 - #773

Open
xu4wang wants to merge 2 commits into
deepcoldy:masterfrom
xu4wang:fix/session-model-follows-bot-config
Open

fix(worker-pool): 模型不再冻结进会话,每次启动跟随 bot 配置#773
xu4wang wants to merge 2 commits into
deepcoldy:masterfrom
xu4wang:fix/session-model-follows-bot-config

Conversation

@xu4wang

@xu4wang xu4wang commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

问题

会话创建时会把 bot 的 model 冻结进 session 记录(sessionAgentConfig),此后每次 resume 都显式传 --model <冻结值>。结果是 dashboard 里配置的模型对存量长会话永久失效

  • 冻结值的来源只有一处——ds.session.model = ds.session.model ?? botCfg.model,也就是「建会话那一刻继承来的 bot 默认」;
  • 唯一会显式写 session.model 的入口是 trigger API 的 options.model,且被 isCodexFamily 门限制,对 claude / gemini / coco 等根本不会写;
  • 所以对绝大多数会话来说,冻结值只是继承来的默认值,却压过了之后人为在 dashboard 做出的显式配置——优先级正好反了。

同一个 bot 上还存在语义分叉:reasoningEffort 没有 botCfg 回填,只认显式来源;model 是「继承也钉死」。两个「运行时档位」两套规矩。

改动

model 退出冻结集合,改为每次 spawn(含 resume)按 live bot 配置解析。 cliId / cliRuntime / cliPathOverride / wrapperCli 的冻结保持不变——那几个被中途换掉会真丢能力(ttadk codex wrapper 掉成裸 codex 会丢网关),而 model 是人主动配的、本就该生效。

解析规则集中在新增的 resolveSessionLaunchModel()src/core/session-model.ts):

  1. DaemonSession.spawnModelOverride —— 显式 per-trigger 覆盖(trigger API options.model,仅 codex 家族),只驻内存
  2. live bot 配置,仅当会话冻结的 cliId 与 bot 当前 cliId 一致——被钉在别的 CLI 上的会话(bot 后来换了 CLI;或 Codex App 线程接管把 cliId 钉成 codex-app)不能被塞进属于另一个 CLI 的 model 串;
  3. 会话自己的历史 model 记录,兜底上面那种 CLI 不匹配的情况。

配套:

  • 不做数据迁移:存量 session.model 记录留在原地、不再被读(Session.model@deprecated,只作历史记录 + 上述第 3 条兜底)。没有需要回滚的写操作。
  • trigger API 的 options.model 修正为名副其实的 per-turn:以前写进持久字段 session.model,一次性调用会变成永久覆盖(文档写的是「仅新建会话生效」);现在落在内存态 spawnModelOverride,daemon 重启后不复活。options.reasoningEffort 行为不变(仍随会话持久化)。
  • 两处展示面改用同一解析函数,与实际启动保持一致:关闭卡里的 resume 命令、本地终端打开命令。前者原本对每个已冻结会话都拿不到 model,会让复制出去的 ttadk 命令静默退化成 ttadk 内置默认模型(glm-5.1),而不是 bot 配的那个。
  • 文档:bots-json(zh/en)说明 model 每次启动解析、改动对存量会话生效;api-task-trigger(zh/en)说明 options.model 只驻内存、不落盘。

影响面

  • 会话类型:普通话题会话、chat 会话、trigger/HTTP 会话、fork 子会话、restore 冷恢复都走同一个 sessionAgentConfig,规则统一;adopt 只观察不 spawn,不受影响。
  • 跨 CLI:所有适配器都从 init 消息拿 model,没有单独的取值路径;ttadk wrapper 的 -m 同步改成 live 配置。
  • Codex App 线程接管daemon.ts):cliId 被钉成 codex-app,与通知 Bot 的 cliId 不一致 → 走规则 3、且遗留记录已清空 → 不会继承通知 Bot 面向别的 CLI 的 model,与改动前行为一致。
  • 不会中途换模型:活着的 CLI 进程不受影响,新配置在下一次启动/恢复时生效。
  • 已知的语义变化:如果 bot 的 model 被清空,存量会话不再传 --model,由 CLI 自行解析(claude --resume 会恢复 transcript 里记录的模型)。这与「没有显式配置就沿用会话自己的历史」一致。

验证

  • pnpm build 通过。
  • 新增 test/session-launch-model.test.ts(8 例)锁优先级三档 + 缺 botCfg 兜底。
  • test/session-lifecycle-start.test.ts 新增两例:同 CLI 的冻结会话 resume 时用当前 bot model(并确认遗留记录未被改写)、显式 per-trigger 覆盖仍然优先;原有「冻结会话不随 bot 配置漂移」的用例改为覆盖 bot 换了 CLI 的情形,仍然绿。
  • test/closed-session-card.test.ts 新增一例:冻结会话的 ttadk resume 命令里 -m 用 live 配置(用非 ttadk 默认值的模型名,避免与内置默认撞车而失去判别力)。
  • test/fork-session.test.ts 新增两例:不把遗留冻结值复制到子会话行;显式 per-trigger 覆盖随运行时会话到子会话。
  • test/trigger-session-root-message.test.ts 改为断言覆盖落在 spawnModelOverride、且 session.model 保持为空。
  • 变异验证:分别把 (a) 冻结行加回、(b) 解析改回 session.model ?? botCfg.model、(c) trigger 不写 spawnModelOverride、(d) 关闭卡改回读 session.model,四种变异各让对应用例转红,无一漏网。
  • 全量 pnpm test:见下方评论贴出的结果。

@xu4wang
xu4wang requested a review from deepcoldy as a code owner August 6, 2026 16:24
@xu4wang

xu4wang commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

全量测试结果(本机 macOS,node 24)

$ pnpm test        # vitest run --project unit
 Test Files  2 failed | 813 passed | 3 skipped (818)
 Duration    87.91s

两个失败都与本改动无关,逐个核过:

文件 单独重跑 干净 upstream/master (b70f160) 上重跑 结论
test/maintenance.test.ts ✅ 通过 满载并发下的超时型 flaky
test/command-handler.test.ts ❌ 同样失败 同样失败expected 'Session: sess-001…' to contain ':8800/s/sess-001',与本分支一字不差) 既有失败,与本改动无关(本机环境相关)

pnpm build 通过。变异验证四项见 PR 描述。

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复审结论:Request Changes。当前改动覆盖了 daemon refork/restore,但仍有 3 条可达的模型状态转换没有闭合:

  1. src/core/worker-pool.ts:950:活 worker 的 /restart / dashboard restart / CLI crash auto-restart 不会重新解析 model。daemon 的 restart IPC 只携带最新 env,worker 随后用旧 lastInitConfig respawn,因此同 CLI 修改 model 后,物理重启仍继续用旧模型。这与“每次启动(含 resume)按当前配置解析”的核心目标直接冲突。
  2. src/daemon.ts:4583:Codex App 完成通知接管只清理 session.model,没有清理新迁移到内存态的 spawnModelOverride。解析规则 1 无条件优先,导致 trigger 的一次性 model 覆盖泄漏到接管后的 codex-app 启动;改动前该值位于 session.model,会被这里正确清掉。
  3. src/core/session-model.ts:51:PR 后创建的会话不再写 session.model,所以 CLI mismatch 时的规则 3 对新会话恒为 undefined。该路径不只来自手改配置:/botconfig set cli 与配置卡通过 applyConfigField 热更新 bot.config.cliId,但没有 dashboard PUT 的 closeCliMismatchedSessionsForBot sweep;旧会话冷停/崩溃后 refork 会丢掉原显式 model,多个依赖启动参数的适配器会退回 CLI 默认。

建议补齐:

  • restart IPC 增加 model 的三态刷新通道(或等效地在每次 worker 内 respawn 前重新解析),覆盖所有 restart 生产者和合并分支;
  • notifier takeover 明确清除 spawnModelOverride
  • 对齐所有 CLI 热切入口的 mismatch sweep,或继续维护一个只用于 mismatch 兜底的历史 model 记录;并为上述状态转换补行为测试。

本地验证:

  • pnpm build
  • pnpm exec vitest run test/session-launch-model.test.ts:8/8 ✅
  • pnpm exec vitest run test/session-lifecycle-start.test.ts:77/77 ✅
  • pnpm exec vitest run test/codex-notifier-adopt-race.test.ts:20/20 ✅
  • pnpm exec vitest run test/codex-notifier-adoption-wiring.test.ts:1/1 ✅

现有测试全绿,但没有覆盖以上三条状态转换。

@xu4wang

xu4wang commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

三条都已修,推在 73a32a8a。逐条回:

1|活 worker 的物理重启不重新解析模型 —— 已修

确认成立,而且不止 /restart:dashboard 重启按钮、dashboard cwd-move、CLI 崩溃 auto-restart 共四个生产者都走同一条「不 refork、worker 用 lastInitConfig 原地 respawn」的路。

修法直接复用仓库里 per-bot env 已有的通道与三分态,保持一致:

  • 新增 latestModelForRestart(ds):字符串 = 用它;null = 当前不该传模型(bot 未配 / 已清空,worker 清快照);undefined = 取不到(bot 已注销),保持快照 = 旧行为;
  • 四个生产者全部捎带 model
  • worker 侧在合并守卫之前覆盖 lastInitConfig.model(与 env merge 同位置,被合并的重复 restart 也要带走更新)。

2|Codex App 接管没清 spawnModelOverride —— 已修

确认成立。接管处补 ds.spawnModelOverride = undefined;,与既有的 delete ds.session.model 并列。

3|新会话缺历史模型记录,CLI mismatch 兜底恒空 —— 已修(选了你给的第二个方案)

确认成立,是真回归。没有走「对齐所有 CLI 热切入口的 mismatch sweep」(那是另一个独立问题,牵扯 /botconfig set cli 与配置卡两条路径的会话生命周期语义,不宜混在本 PR 里),而是按你给的备选维护一个只用于 mismatch 兜底的记录

session.model 语义从「创建时冻结值」改为**「本会话上次实际启动用的模型」——每次 spawn 用解析结果回写;显式的 per-trigger 覆盖不**写入(一次性值持久化正是本 PR 要消灭的错误)。它只在规则 3 被读,不参与正常优先级,因此不会把原 bug 带回来,而新老会话的兜底都成立了。

测试

新增 test/restart-live-worker-model.test.ts(14 例):三分态纯函数(含 override 优先、CLI mismatch)、live-worker restart 消息体、四个生产者的 wiring、worker 侧 merge 位置与 null 语义、接管清覆盖。

session-lifecycle-start 新增一例端到端串起规则 2/3:会话在 codex bot 下首次启动记录 glm-5.1 → bot 换成 claude-code + opus → 该会话 refork 仍用 glm-5.1(既不掉 CLI 默认,也不串到另一个 CLI 的模型)。

顺带更新了两个被消息体变化波及的既有测试:crash-loop-diagnostic(restart 消息全等断言)、restart-worker-null-reattach(source pin)。

变异验证:restart 不带 model / worker 不 merge / 接管不清覆盖 / 去掉启动记录回写,四种变异分别让对应用例转红。

pnpm build ✅   tsc --noEmit ✅
pnpm test  →  4 failed | 812 passed | 3 skipped (819)

4 个失败逐个核过,均与本改动无关:command-handler.test.ts 在干净 upstream/master(b70f160) 上报一模一样的错(既有失败);codex-app-runner.integrationworker-argv-reaction-status.integrationworkflow-c0-isolation 单独重跑全绿(满载超时型 flaky,同一份改动的两次全量跑出的失败集合不同也印证了这点)。

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

结论:仍需修改。73a32a8a 已补上普通 live-worker restart 的 model 携带、notifier 接管清理 override,但还有三条可达缺口:

  1. live-worker respawn 没有同步 rule 3 的历史记录。 sessionAgentConfig() 只在 daemon refork 时回写 session.model;四个 restart 生产者只把最新 model 发给 worker,worker 也只更新 lastInitConfig.model。因此会话先以 A 启动,bot 改成 B 后 live /restart 确实启动 B,但持久记录仍是 A。随后通过 /botconfig set cli / 配置卡热切 CLI(这些路径没有 mismatch sweep),下一次 /restart 的 CLI-mismatch 规则会从旧记录取回 A,刚切到 B 的旧 CLI 会话又退回 A。新增测试只断言了 IPC 的 model: B,没有断言成功 respawn 后历史记录与 B 收敛。请补完整状态转换及 A→B live restart→切 CLI→仍保持 B 的回归测试;一次性 spawnModelOverride 仍不得落盘。

  2. crash-loop park 后的消息触发重试仍读旧 model。 第四次 crash 后 worker 被保留在 crashDiagnosticStopped;用户此时修改 bot model,再发下一条消息,worker.ts 直接用 {...lastInitConfig, resume:true}spawnCli,daemon 没有 restart IPC 可携最新 model。这条是现有的正式恢复路径,也违反“每次 spawn 跟随 live 配置”。需要给该 retry 路径补刷新通道与测试。

  3. fork 丢失了新定义下必需的 mismatch 历史。 当前 forkSession 明确不复制 session.model。当源会话的冻结 CLI 已与 bot 当前 CLI 不同,源会话靠 rule 3 保留其历史 model;child 继承旧 cliId 却没有历史 model,首个 fork spawn 会落到 undefined/default。新 resolver 在 CLI 匹配时始终让 live bot model 优先,因此复制这条历史记录不会复活旧冻结 bug;反而是 mismatch fork 所必需。请补“bot 热切 CLI 后 fork 旧会话仍沿用其最后模型”的测试。

另外,当前 head 与 origin/master@725d7e47e 的 merge-tree 有 6 个冲突,且不全是字段并集:

  • restart IPC 要保留 master 的 reason 和本 PR 的 model;dashboard operator 发送者需同时带 reason:'operator' + 最新 model,crash sender同理保留 reason:'cli_crash'
  • trigger-session.ts 必须把本 PR 的 in-memory spawnModelOverride 语义移植进 master 新增的原子 first-owner claim,不能退回持久化 session.model
  • fork-session 冲突需保留 master 的 cliRuntime 深拷贝;在上述第 3 点修正后,同时保留历史 model 与 runtime override 的正确分层。

Codex App 同 CLI 的通知接管边界本身不是问题:runner 的 thread/resume 明确不发送 model,恢复线程持久化的 model/provider/effort;只有 resume 失败转 fresh thread/start 时才使用 bot 的 model,这与同 CLI 配置一致。

本地验证:pnpm build 通过;restart-live-worker-model 14、session-lifecycle-start 78、crash-loop-diagnostic 3、restart-worker-null-reattach 10、codex-notifier-adopt-race 20、fork-session 18 均隔离通过。上述缺口是现有测试未覆盖的状态序列。

xu4wang added 2 commits August 9, 2026 15:09
会话创建时会把 bot 的 model 冻结进 session 记录,此后每次 resume 都显式
`--model <冻结值>` 启动,导致 dashboard 里配置的模型对**存量长会话永久失效**:
冻结值只是「建会话那一刻继承来的默认」,却压过了之后人为做出的显式配置,
优先级正好反了。

改为:model 在**每次 spawn(含 resume / 物理重启)时按 live bot 配置解析**,不再
进冻结集合。`cliId` / `cliRuntime` / `cliPathOverride` / `wrapperCli` 的冻结**保持
不变**——那几个被中途换掉会真丢能力(`ttadk codex` wrapper 掉成裸 codex 会丢网关),
而 model 是人主动配的、本就该生效。也不做数据迁移:存量记录留在原地、语义改为
「上次实际启动用的模型」。

解析优先级集中在新增的 `resolveSessionLaunchModel()`(core/session-model.ts):
1. `DaemonSession.spawnModelOverride` — 显式的 per-trigger 覆盖(trigger API
   `options.model`,仅 codex 家族),**只驻内存**;
2. live bot 配置,**仅当会话冻结的 cliId 与 bot 当前 cliId 一致**——被钉在别的 CLI
   上的会话(bot 后来换了 CLI、或 Codex App 线程接管把 cliId 钉成 codex-app)不能
   被塞进属于另一个 CLI 的 model 串;
3. 会话记录的「上次实际启动用的模型」,仅兜底上面那种 CLI 不匹配的情况。
   (`/botconfig set cli` 与配置卡热切 cliId 时没有 dashboard PUT 那条
   closeCliMismatchedSessionsForBot 清扫,这类会话确实能活到下一次 refork。)

覆盖全部启动路径:
- daemon refork / restore:`sessionAgentConfig` 解析并回写记录;
- 活 worker 的物理重启(`/restart`、dashboard 重启按钮、dashboard cwd-move、
  CLI 崩溃 auto-restart):restart IPC 捎带最新 model,沿用 per-bot env 已有的
  三分态(字符串=用它 / null=当前不该传 / undefined=取不到就保持快照),worker 在
  **合并守卫之前**覆盖 `lastInitConfig.model`。

trigger API 的 `options.model` 顺带修正为名副其实的 per-turn:以前写进持久字段
`session.model`,一次性调用会变成永久覆盖(文档写的是「仅新建会话生效」),现在落在
内存态 `spawnModelOverride`;Codex App 线程接管会显式清掉它。
`options.reasoningEffort` 行为不变(仍随会话持久化)。

影响面
- 会话类型:普通话题会话、chat 会话、trigger/HTTP 会话、fork 子会话、restore
  冷恢复都走同一个 `sessionAgentConfig`;adopt 只观察不 spawn,不受影响。
- 跨 CLI:所有适配器统一从 init 消息拿 model,规则同一条;ttadk wrapper 的 `-m`
  取值同步改成 live 配置(关闭卡里给出的 resume 命令原本会退化成 ttadk 内置默认
  模型,而不是 bot 配的那个)。
- 展示面:关闭卡 resume 命令、本地终端打开命令都改用同一解析函数,与实际启动一致。

验证
- `pnpm build` / `tsc --noEmit` 通过。
- 新增 `test/session-launch-model.test.ts`(8 例)锁优先级;
  `test/restart-live-worker-model.test.ts`(14 例)锁 restart 三分态、四个生产者
  wiring、worker 侧 merge 位置与 null 语义、接管清覆盖;
  `session-lifecycle-start` / `closed-session-card` / `fork-session` /
  `trigger-session-root-message` 各有新增或改写用例。
- 变异验证:加回冻结行 / 解析改回 `session.model ?? botCfg.model` / trigger 不写
  内存字段 / 关闭卡改回读 `session.model` / restart 不带 model / worker 不 merge /
  接管不清覆盖 / 去掉启动记录回写,八种变异分别让对应用例转红。
复审第二轮指出三条仍未闭合的状态转换,逐条修:

1. **live restart 只刷了启动参数、没刷记录。** 四个 restart 生产者把最新 model 发给
   worker,但 `session.model`(rule 3 的兜底记录)只在 daemon refork 那条路回写:
   会话以 A 启动 → 改成 B → live `/restart` 起了 B,记录仍是 A → 之后热切 CLI(那些
   没有 mismatch 清扫的入口)→ rule 3 把 A 取回来,B 又丢了。
   把回写抽成 `recordLaunchModel()`,`sessionAgentConfig` 与 respawn 解析共用;
   `latestModelForRestart` 更名 `latestModelForRespawn`(它现在服务两类 respawn),
   解析时一并收敛记录。

2. **crash-loop park 后由消息触发的恢复重启读旧快照。** 第四次崩溃后 worker 停在
   `crashDiagnosticStopped`,下一条消息直接 `{...lastInitConfig, resume:true}` 起 CLI,
   这条路没有 restart IPC 可携带新值。给 `message` IPC 加同一套三分态 model 刷新,
   worker 在消息处理开头覆盖快照(在 park 重试分支之前);对已在跑的 CLI 无影响。

3. **fork 反而需要复制那条记录。** 上一版刻意不复制 `session.model` 是错的:源会话
   若已 CLI 不匹配、靠 rule 3 保住模型,子会话继承旧 `cliId` 却没有记录,首个 fork
   spawn 会掉到 CLI 默认。复制不会复活旧冻结 bug——CLI 匹配时 live 配置永远优先。

同时 rebase 到上游 master `725d7e47`,手工解 6 个文件的冲突(非字段并集处):
restart IPC 同时保留上游的 `reason: 'operator' | 'cli_crash'` 与本 PR 的 `model`;
`trigger-session` 把内存态 `spawnModelOverride` 移植进上游新增的原子 first-owner
claim(不退回持久化 `session.model`);`fork-session` 保留上游的 `cliRuntime` 深拷贝
并与本次的记录复制分层。

验证
- `pnpm build` / `tsc --noEmit` 通过。
- `restart-live-worker-model` 扩到 20 例,新增:respawn 回写记录、一次性覆盖不入记录、
  以及完整状态序列「A → live restart 到 B → 热切 CLI → 仍是 B」;另加 crash-park
  重试通道的三条(worker 侧 merge 语义、merge 在重试分支之前、两个 message IPC
  生产者都捎带)。
- `fork-session` 把「不复制」那条反写为「复制记录,CLI 不匹配的 fork 仍用自己的模型」。
- 变异验证:respawn 不回写记录 / message 不带 model / worker 不 merge / fork 不复制
  记录,四种变异分别让对应用例转红。
@xu4wang
xu4wang force-pushed the fix/session-model-follows-bot-config branch from 73a32a8 to 883eda8 Compare August 9, 2026 08:23
@xu4wang

xu4wang commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

三条都已修,并已 rebase 到 725d7e47(6 个冲突手工解完)。逐条回:

1|live restart 没有同步 rule 3 的历史记录 —— 已修

确认成立。把回写抽成 recordLaunchModel()sessionAgentConfig(daemon refork)与 respawn 解析共用;latestModelForRestart 更名为 latestModelForRespawn(它现在服务两类 respawn),解析时一并收敛记录。一次性 spawnModelOverride 仍然不入记录、不落盘。

补了你要求的完整状态序列测试:A 启动 → 改成 B → live /restart(断言 IPC model: Bsession.model === 'B')→ bot 热切 CLI → 该会话仍解析为 B

2|crash-loop park 后的消息触发重试 —— 已修

确认成立,worker.ts 那条 Message received after crash-loop stop 分支直接用 {...lastInitConfig, resume:true} 起 CLI。

message IPC 加了同一套三分态 model 刷新,worker 在消息处理开头覆盖快照(在重试分支之前,对已在跑的 CLI 无影响)。

一个实现细节值得说明:第一版我无条件在每条 message 上带 model,被三个既有测试判红card-integrationsession-lifecycle-start 两处都对 message 消息体做全等断言)。那正是它不该无条件带的信号——普通轮次根本不会重新 spawn,多带一个字段纯属噪音。改为按会话上的 crashDiagnosticParked 标记开关:daemon 发 park_diagnostic 前置位,worker 再次 ready 时清除,只有真正处于 park 态的会话才在消息里捎带模型。

行为测试落在 crash-loop-diagnostic:4 次崩溃 park → 期间改 bot model → 下一条消息的 IPC 带 model: 'opus'ready 后标记清除。

3|fork 需要复制 mismatch 历史 —— 已修

确认成立,恢复 childSession.model = ds.session.model。正如你指出的,新 resolver 在 CLI 匹配时始终 live 优先,所以复制不会复活旧冻结 bug。原来那条「不复制」的用例反写为:bot 已切到 claude-code/opus、源会话钉在 codex 且记录 gpt-5.6 → 子会话行拿到 cliId: codex + model: gpt-5.6(既不是 undefined,也不是 bot 的 opus)。

冲突解法(按你点的三处)

  • restart IPC{ type:'restart'; reason?: 'operator'|'cli_crash'; attemptId?; updateWorkingDir?; env?; model? }——两边字段并存;dashboard operator 端 reason:'operator' + model: latestModelForRespawn(ds),crash 端保留 reason:'cli_crash' 同样带 model;crash-loop-diagnostic 的全等断言相应变成 { type:'restart', reason:'cli_crash', env:null, model:null }
  • trigger-session.ts:内存态 spawnModelOverride 移植进 master 新增的原子 first-owner claim 内部——在 claim 里算出 triggerModelOverride,随 newDs 字面量落到运行时会话;session.model 不再被写。reasoningEffort 仍按原样持久化。
  • fork-session:保留 master 的 cliRuntime 深拷贝({...runtime, update:{...update}}),与本次恢复的 childSession.model 记录复制并列——运行时覆盖仍只走 childDs.spawnModelOverride,三层各归各位。

另外谢谢澄清 Codex App 同 CLI 接管那条(thread/resume 不发 model),本 PR 未对该路径做额外处理,与你的结论一致。

验证

  • pnpm build / tsc --noEmit 通过。
  • restart-live-worker-model 21 例(新增:respawn 回写记录、一次性覆盖不入记录、A→restart B→切 CLI→仍 B、park 标记的置位/清除、两个 message IPC 生产者的 wiring);crash-loop-diagnostic 6 例(含 park 期间改模型的行为测试);fork-session 20 例;session-launch-model 8 例;session-lifecycle-start 78 例。
  • 变异验证(本轮 6 项,全部转红):respawn 不回写记录 / message 不带 model / worker 不 merge / fork 不复制记录 / park 时不置标记 / ready 时不清标记。
pnpm build ✅   tsc --noEmit ✅
pnpm test  →  3 failed | 870 passed | 4 skipped (877 files)

3 个失败文件全部在干净的 725d7e47 上逐个复跑,结果一致,与本改动无关:

文件 本分支 干净 725d7e4
command-handler.test.ts expected 'Session: sess-001…' to contain ':8800/s/sess-001' 同样失败(同一断言)
worker-argv-reaction-status.integration.test.ts 同样 2 例失败 同样 2 例失败(同名)
codex-app-runner.integration.test.ts 1 例失败 1 例失败(每次失败的用例名不同 → 超时型 flaky)

分支 head:883eda8d(已 rebase 到 725d7e47 + 本轮三条修复)。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants