Skip to content

fix(agent-core-v2): clear inherited cron tasks at the fork record - #4083

Merged
sailist merged 1 commit into
mainfrom
bug-277-09-29-fork-cron-clear
Sep 29, 2026
Merged

sailist merged 1 commit into
mainfrom
bug-277-09-29-fork-cron-clear

Conversation

@sailist

@sailist sailist commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator

Requirement or Bug

fork 后的会话会继承源会话的定时任务(cron):源会话与 fork 两侧各跑一个调度器,同一定时任务触发两次(线上双触发反馈;无 issue,内部反馈已登记)。

Bug Reproduction Steps

  1. 在会话 A 中用 CronCreate 创建一个定时任务。
  2. fork 会话 A 得到会话 B(web 或 TUI 的 fork)。
  3. 两侧会话同时挂载(如 web 同时打开):同一任务在 A、B 各触发一次;B 的 CronList 也能看到并操作 A 的任务副本。

Root Cause

根本修复,非绕过。v2 的 cron 任务以 cron.add/delete/cursor durable 记录存在于 wire.jsonl,而 fork 的定义就是拷贝 wire——任务表因此被 fork 原样继承。goal 早已处理同类问题:fork 时往 fork 侧 wire 追加 forked 标记记录,goal fold 到它即清零并投递一次性提醒;cron 从未做过这个决策,属于设计盲区(v1 时代 cron 就明确否决了跨 session 持久化,持久化目标仅为同一 session resume 恢复)。修复与 goal 同构:cron 的 fold 事件加入 forked 标记,fold 到即在 fork 点清空任务表并投递一次性提醒;调度器表空即空转,无需任何特判。

Code Changes

 packages/agent-core-v2/src/
 ├── session/agentLifecycle/
+│   └── forked.ts            # Forked 事件类(type 'forked'),goal 与 cron 共享
 ├── features/goal/
-│   └── goalOps.ts           # GoalForked 移出(fold 语义不变)
 └── features/cron/
     ├── cronOps.ts           # CronModelState:裸 Map → { tasks, forkNotice }
     └── cronService.ts       # fold Forked / ContextAppendMessage + 一次性 reminder

fold 逻辑(伪代码 diff):

 on(record)
   cron.add / cron.delete / cron.cursor   # 原样维护任务表
+  forked
+    forkNotice.reminderPending ||= tasks.size > 0
+    tasks.clear()                        # forked 记录只存在于 fork 侧 wire
+  context.append_message (variant = cron_fork_cleared)
+    forkNotice.reminderPending = false   # 交付确认,恰好一次且不丢
  • reminder 在 cronEffects 挂载时投递(仅 main agent,与调度器同范围),文案三句:本 fork 没有定时任务 / 源会话的任务仍在源会话继续运行 / 如有需要可在此重建。
  • GoalForked 更名 Forked 并上移至 session 域(Event2 registry 中同 type 只能注册一个类,必须共享);apps/vis/server 引用同步;index.ts 增加导出。
  • wire 词表零变更:forked / cron.* 记录的 type 与 payload 不变(wire-manifest 仅 owner 路径随行),旧会话状态连续。

Behavior Changes and Affected Users

Behavior Before After Who relies on the old behavior Escape hatch
fork 后的 cron 任务 fork 继承源会话任务表,源会话与 fork 两侧各触发一次(双触发) fork 侧任务表在 fork 点清空,仅源会话继续触发 依赖"fork 后继续收到源会话定时提醒"的用户(TUI / web fork 出的会话) 无——fork 侧首个 turn 注入一次性提醒,可按需重建;请维护者在本 PR 显式拍板
fork 侧 CronList / 任务操作 列出并可操作源会话任务的副本(删除只影响副本,极易误解) 列表为空,新建任务只属于本 fork 同上 同上
新增 prompt 文本:cron_fork_cleared reminder(三句) 无 fork 点存在任务的 fork 会话,首个 turn 上下文注入一次 无(新增,恰好一次投递) n/a
agent-core-v2 包导出 GoalForked 从 features/goal 导出 更名 Forked,改从 session/agentLifecycle 导出 内部包不发布;仓库内唯一消费方 vis-server 已同步 n/a

受影响模块与测试覆盖:

  • packages/agent-core-v2/test/features/cron/sessionCron.test.ts:fork 边界四分支(fork 点有任务→清除+reminder、无任务→不提醒、fork 点之后新建任务→存活、已交付→不重投)+ 既有 wire replay 覆盖(两个用例合并,it 数不变)
  • packages/kap-server/test/sessions.test.ts:fork clears inherited cron tasks while the source keeps them——真实 :fork API + resume 的 e2e,由钉住旧行为的用例改写(it 数不变),同时钉住源会话不受影响
  • goal 的 fork 行为不变(goal.test.ts fork boundaries 用例随全量通过;本次仅改其事件类引用)
  • m0-dev 远程全量 16664 passed / 1 failed:唯一失败为 sessionIndex 周期 tick 计数(计时敏感),本地 3/3 复跑通过,与本改动无代码交集,判定环境抖动

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue (external PRs: issue must have a maintainer's /approve).(内部 PR,无 issue,线上反馈已登记)
  • I have added tests that prove my feature works.
  • The behavior-change table above is complete, and every removed behavior or flipped default is named in the changeset and either has an escape hatch or was explicitly approved by a maintainer in this PR.(无 escape hatch,请在本 PR 显式批准)
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.(工具描述未声称 fork 继承任务,无文档失真)

Fork copies the wire journal, so cron tasks persisted as cron.add
records were inherited by the fork and fired from both sessions.
Fold the forked marker record into the cron actor: the task table is
cleared at the fork point and a one-time cron_fork_cleared reminder is
delivered (tracked by a durable forkNotice flag confirmed via
ContextAppendMessage), mirroring the goal feature's fork handling.
The forked event class moves from features/goal to
session/agentLifecycle so both features share it.
@changeset-bot

changeset-bot Bot commented Sep 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 2e58b9a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Sep 29, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@2e58b9a
npx https://pkg.pr.new/@moonshot-ai/kimi-code@2e58b9a

commit: 2e58b9a

@sailist
sailist merged commit f409caa into main Sep 29, 2026
16 checks passed
@sailist
sailist deleted the bug-277-09-29-fork-cron-clear branch September 29, 2026 11:10
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