Skip to content

fix(scheduler): 派发前撤回的提示直接删除,并定期清掉历史软隐藏 - #7828

Merged
MagicLizi merged 3 commits into
makecindy:mainfrom
elfcv:fix/scheduler-prompt-discard-cleanup
Nov 1, 2026
Merged

MagicLizi merged 3 commits into
makecindy:mainfrom
elfcv:fix/scheduler-prompt-discard-cleanup

Conversation

@elfcv

@elfcv elfcv commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

这次改了什么

摘要

例行任务在送达模型前被撤回时,提示已经写入 messages,但只标记 rewind_at。反复撤回会留下大量软隐藏行,把本地库撑到数百 MB,拖慢本机和远程列表。这次对已证实未送达的调度提示先软隐藏、再在同一事务删除;调度器启动后每 10 分钟按批清掉历史上已经隐藏、且能证明从未送达的 scheduler 行。可见消息、用户手动回退的历史和其他回退墓碑不动。

历史清理只删同时满足两条的行:agent_meta.executionModel 为空(派发时 onModelDispatched 会写入它),且同会话里没有别的行共用同一个 rewind_at(rewind.commit 给提示和其后所有消息盖同一个时间戳,手动回退不会留下落单墓碑)。每次清理沿 idx_messages_rewind_at 只扫一个 500 行窗口并返回游标,扫描量与库里普通墓碑的数量无关;没有新增索引或 migration。

变更类型

  • fix 缺陷修复
  • test 测试

范围

UI 变化

  • 不涉及。
  • 引用的设计规范:不涉及。未改 renderer、布局、交互或文案。

怎么验证的

自动验证

最新 head 2966bcdfa7(已 rebase 到 origin/main 19dad9c225),独立 Linux aarch64:

pnpm --filter desktop exec vitest run src/main/localDb/ipc/__tests__/messagesClearRace.test.ts src/main/scheduler-host/__tests__/rewoundSchedulerPromptCleanup.test.ts --maxWorkers=2
结果:9/9 通过。命令墙钟 4.43 秒(tests 28ms)。

pnpm --filter desktop exec vitest run src/main/scheduler-host/__tests__/runnerSilentNotify.test.ts src/main/scheduler-host/__tests__/runnerBackgroundTasks.test.ts src/main/scheduler-host/__tests__/runnerModelSelection.test.ts src/main/maker-ipc/__tests__/agent-input-coordinator.test.ts --maxWorkers=2
结果:814/814 通过,4 文件。命令墙钟 21.83 秒。

pnpm --filter desktop exec vitest run src/main/localDb/client/__tests__/WorkerThreadTransport.test.ts
结果:19/19 通过。

pnpm --filter desktop exec tsc --noEmit -p tsconfig.json
结果:通过(exit 0)。

node scripts/check-dco.mjs --base origin/main --head HEAD
结果:3 个 commit 均已签名。

pnpm check:test-hygiene
结果:PASS(357 files within baseline)。

git diff --check origin/main...HEAD
结果:通过。

测试价值:

  • 生产入口是 rewindPersistedUserMessageAfterClear、purgeRewoundSchedulerPrompts 和调度器启动后的清理。
  • 最小失败输入:落单且无 executionModel 的隐藏 scheduler 行必须被删;手动回退留下的 prompt+reply 对(同一 rewind_at)、带 executionModel 的落单行、可见 scheduler 行和普通回退墓碑必须留下;discard 必须先触发 rewind 向量清理再删除。
  • 有界扫描:620 条墓碑时第一窗口返回 cursor=1499 且不删,第二窗口删掉窗口外的两条并返回 null;一页删满 limit 时重扫同一窗口。sweeper 用例锁住游标跨批传递、扫完即停、下一轮归零、单轮 5 批上限。
  • tier:unit。内存 SQLite,不访问真实账号。

定向 ESLint:改动的 7 个文件里,WorkerThreadTransport.ts 第 401/419/1272 行与 messagesClearRace.test.ts 第 11 行有既有 no-useless-escape / no-explicit-any 报错,均不在本 PR 改动范围内,本轮没有新增 lint 错误,也不宣称全文件 lint 通过。

手工验证

不涉及。没有导入 2GB 的真实伙伴库做清理计时。

未执行的验证

  • 未在含数十万软隐藏行的真实库上测量清理耗时。
  • 本机结果不代表 GitHub CI;以新 head 的 CI 为准。

风险

风险分类

  • SQLite / migration(只删行,不改表结构、不加索引)
  • 权限 / 安全 / 用户数据

影响与回滚

  • 影响范围:只删除 role=user、rewind_at 非空、agent_meta.origin.kind=scheduler、无 executionModel、且同会话没有其他行共用该 rewind_at 的行,以及本次被证实未送达 vendor 的调度提示。不改表结构。
  • 残余边界:送达后模型没有留下任何持久化行、且 executionModel 落地前的旧版本写入、随后又被用户手动回退的调度提示,与未送达行无法区分,仍会被删;这类行本来不在会话里显示。
  • 回滚 / 降级方式:回退本提交。已删除的历史隐藏行无法从本提交恢复,但它们本来就不出现在会话里。

提交前检查

  • 已 review 完整 diff
  • 每个 commit 都带 DCO 签名
  • UI 不涉及
  • 未提交凭证、令牌或授权文件
  • 已核对受影响的行为
  • 已确认测试结果

@elfcv
elfcv requested a review from a team as a code owner October 29, 2026 08:26
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-29T08:32:24.106481Z
We couldn’t complete this review. Comment @codex review to try again.
f24fc2a PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@greptile-apps

greptile-apps Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The changes delete a narrow set of hidden, undelivered scheduler messages from the local database.

合并前需要收窄历史清理条件,避免永久删除用户手动回退留下的调度记录。

Findings

  1. P1 手动回退历史被误删 ▶
  2. P2 清理反复扫描隐藏历史 ▶
Fix with agent prompt
### Issue 1
apps/desktop/src/main/localDb/worker/opHandlers/tx.ts:1991-1993
用户手动回退已送达的调度提示后,`rewind.commit` 会设置 `rewind_at`,但保留 `agent_meta`。这些消息也会命中这里,被后台永久删除。这超出了“不动用户手动回退墓碑”的范围,原本保留的历史记录无法恢复。

请用能证明未送达的标记筛选,不能只凭调度来源和隐藏状态删除。`WorkerThreadTransport.ts` 中的同一查询也需要同步调整。

### Issue 2
apps/desktop/src/main/localDb/worker/opHandlers/tx.ts:1989-1996
`LIMIT` 只限制删除数量,没有限制查找工作量。现有索引只有单列 `rewind_at`,没有按调度来源筛选的索引。如果库里有大量普通回退墓碑,却很少或没有待清理的调度行,每次查询仍要遍历这些墓碑并检查 JSON。十分钟后又从头检查,会占用其他读写共用的数据库线程。

建议增加匹配条件的部分索引,或用有界游标限制每次检查的行数。`WorkerThreadTransport.ts` 中的同一查询也需要同步调整。

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

本次改动让已确认未送达的调度提示在隐藏后立即删除,并在调度器启动后定期清理历史隐藏提示。

  • Confirmed-undelivered scheduled prompts are removed from message history.
  • The scheduler periodically removes older hidden scheduler prompts.

Reviews (1) · Last reviewed commit: "fix(scheduler): 派发前撤回的提示直接删除,并定期清掉历史软隐藏" · Reviewed by Greptile

@MagicLizi MagicLizi added the touches:core 改动碰到架构核心路径(review-pr 自动维护,仅展示) label Oct 29, 2026
@MagicLizi

Copy link
Copy Markdown
Contributor

@elfcv 这个 PR 因 review conversation 未 Resolve,已等待约 48 小时,当前仍有 2 条未解决讨论。

如果已按意见修改并验证,请在对应讨论中说明处理结果,再点击 Resolve conversation;如果问题还未解决,请先处理。仅回复“已修复”不会关闭讨论。

全部解决后,下一轮自动审查会重新检查 PR;仍需满足代码审查、CI 和其他合并条件。

elfcv added 3 commits November 2, 2026 03:18
例行任务在送达模型前被撤回时,只写 rewind_at 会让 messages 无界增长。先软隐藏以触发向量清理,再在同一事务删除该行。调度器启动后按批删除已经隐藏的 scheduler 行,不碰可见消息和其他回退墓碑。

Signed-off-by: elfcv <cnxpfcv@gmail.com>
The direct-send rewind callback does not accept discard. Passing options failed typecheck and must not delete a user message.

Signed-off-by: elfcv <cnxpfcv@gmail.com>
…indows

The background purge matched every hidden scheduler prompt. A manual
rewind of a delivered prompt leaves the same shape (rewind_at set,
agent_meta kept), so that history would have been deleted too. Now a row
is only removed when nothing proves delivery: no executionModel was
recorded at dispatch and no other row in the session shares its
rewind_at (rewind.commit stamps the prompt and everything after it with
one timestamp, so a manual rewind never leaves a lone tombstone).

Each purge also walks idx_messages_rewind_at in 500-row windows and
returns a cursor; the sweeper carries it across batches and restarts
from zero on the next sweep. The scan cost per call no longer depends on
how many ordinary tombstones sit in the database.

Signed-off-by: elfcv <cnxpfcv@gmail.com>

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PR #7828 审查报告(auto / standard 档)

  • PR: #7828
  • 标题: fix(scheduler): 派发前撤回的提示直接删除,并定期清掉历史软隐藏
  • head: 2966bcd(3 commits)
  • base: d3e072f(main)
  • diffHash: f293869384c72427a1c6c21b0d3303debb701c87930e83f6f2f75339c6843b93(冻结证据一致)

Findings

没有 P0/P1。

不阻断的 P2 级观察(按口径不发 thread,仅记录):

  1. apps/desktop/src/main/localDb/ipc/messages.ts:1296 顶部注释仍写「The row stays as a rewind tombstone so the same clientId remains idempotent」,与新增 discard 选项(同 clientId 二次重试时按 rewind_at IS NULL 查不到即幂等返回)有轻微文字出入;调用点 agent-input-coordinator.ts:5471 的注释已补上「The row is deleted, not tombstoned」。
  2. purgeRewoundSchedulerPrompts 清 sessions.list_preview/list_preview_role/list_message_count 后不广播会话行更新;被清行本就不在会话视图出现,仅其它窗口内存里的计数需等刷新才一致,属可选优化。

Rule coverage — 读取的规则文件及逐项结论

  • docs/dev-rules/database-and-migrations.md(Review 清单 1–7):本 PR 无 schema 变化、无 migration、无索引新增,纯运行时代码调整(清单第 1 条的「只需运行时代码调整」路径);不触碰已冻结 NNNN_*.sql / journal / snapshot;未改连接初始化顺序(pragma / UDF / TEMP)。删除行为运行期 DELETE,不涉 migration replay。
  • docs/dev-rules/engineering-conventions.md(Review 清单 1–8):无裸 console.log;未新增渲染层 IPC handler(message.purgeRewoundSchedulerPrompts 是既有 db tx 通道的内部 worker 事务,仅 main 侧 sweeper 调用);main 侧新逻辑带主路径 + 边界测试(messagesClearRace.test.ts、rewoundSchedulerPromptCleanup.test.ts);新测试用内存 SQLite、fake timers,无外网/固定端口/共享临时路径/真实子进程;纯 TS + SQLite,无路径/子进程/快捷键,macOS/Windows 行为一致;无 UI 文案;新核心逻辑带职责注释。
  • docs/dev-rules/architecture-invariants.md(Review 清单 1–4):rewoundSchedulerPromptCleanup.ts 顶层静态 import,main 进程无新增动态 import();不涉 package 反向依赖与布局树。
  • docs/dev-rules/credentials-and-local-storage.md(Review 清单 1–6):不写任何凭证/令牌;不新增落盘路径;删除对象仅限 role='user'、rewind_at 非空、agent_meta.origin.kind='scheduler'、无 executionModel、且同会话无其它行共用同一 rewind_at 的软隐藏行,属用户数据删除路径,判据保守且可回滚(回退提交即停)。
  • docs/dev-rules/development-workflow.md P0/P1 口径:无红线、崩溃、跨平台失效或凭证泄露;数据删除是本修复目标本身且限定可证明未送达的行,不构成数据丢失。
  • AGENTS.md 安全底线与测试门槛:无凭证入仓;新增测试经真实生产入口(rewindPersistedUserMessageAfterClear、purgeRewoundSchedulerPrompts、sweeper)验证可观察结果,最小失败输入与 tier 已在 PR 描述说明;pnpm check:test-hygiene PASS。
  • UI / DESIGN.md:不适用(format.uiCodeFiles 为空,无渲染层改动)。

固定问题逐条

  1. 单一目的(撤回即删 + 历史未送达软隐藏清理)与 Description、测试、风险段一致,无夹带(15 个文件全部服务该目的)。
  2. 共享符号读方核查:rewindPersistedUserMessageAfterClear 全部 4 个调用点逐一核对——runner.ts:2269(派发前守卫错误)与 agent-input-coordinator.ts:5495(PRECONDITION_FAILED 准备拒绝)带 discard:true;agent-input-coordinator.ts:7399(clear-race)与 makerSendTransaction.ts:1792(clear-race,类型保持两参)不带 discard、保留墓碑,语义正确。message.rewindUserAfterClear / message.purgeRewoundSchedulerPrompts 在 WorkerThreadTransport.ts 与 worker/opHandlers/tx.ts 两份实现 SQL 一致,类型表 tx/types.ts 同步。隐藏时的媒体引用回收(removeMediaRefs / removeSessionAttachmentRefIfUnreferencedByLiveMessage)在 rewind 路径统一执行,历史清理删行前引用已在隐藏时回收,不留悬挂引用。
  3. 权限/用户数据/回滚:删除判据三重收敛(scheduler 来源 + 无 executionModel + 落单 rewind_at),手动回退墓碑与已送达行均保留(测试逐一锁住);回滚为回退提交。
  4. 无新依赖、无危险写法、无数据外发;security 内容门 0 命中(standard 档不跑 security-scan.mjs)。
  5. 测试证据:见 Verification,本地实测通过。
  6. 无组织/个人流程塞入 Core。
  7. 无 UI 证据要求。
  8. 不涉 Mobile 冷更。

Verification — 实际运行的命令、结果、未执行项及原因

本地(审查 worktree,pnpm install --frozen-lockfile --prefer-offline 后):

  • pnpm --filter desktop exec vitest run src/main/localDb/ipc/__tests__/messagesClearRace.test.ts src/main/scheduler-host/__tests__/rewoundSchedulerPromptCleanup.test.ts --maxWorkers=2 → exit 0,9/9 通过(与 PR 声称一致)。
  • pnpm --filter desktop exec vitest run src/main/scheduler-host/__tests__/runnerSilentNotify.test.ts src/main/scheduler-host/__tests__/runnerBackgroundTasks.test.ts src/main/scheduler-host/__tests__/runnerModelSelection.test.ts src/main/maker-ipc/__tests__/agent-input-coordinator.test.ts --maxWorkers=2 → exit 0,814/814 通过(与 PR 声称一致)。
  • pnpm --filter desktop exec vitest run src/main/localDb/client/__tests__/WorkerThreadTransport.test.ts → exit 0,19/19 通过。
  • pnpm check:test-hygiene → exit 0,PASS(357 files within baseline)。
  • git diff --check 19dad9c225..HEAD(PR 自身 3 个 commit 范围)→ exit 0。说明:在本 worktree 用 git diff --check origin/main...HEAD 会报 packages/maker-shared/.../historyWorkLines* 两处 EOF 空行,但该文件不在本 PR 的 15 文件 diff 内,来自 base 侧前移的其它合入,与本 PR 无关。

未执行项:

  • tsc --noEmit(desktop):本机三次尝试(默认、8G、10G heap)均以 JS heap OOM(SIGABRT)崩溃,是审查环境内存限制而非类型错误,未取得本地类型检查结论;类型正确性以 GitHub client-ci 全绿(completed/success)与 PR 作者的独立环境 tsc 记录为据,如实标注本项未在本机复验。
  • security-scan.mjs:standard 档不执行(heavy 专有)。
  • 真实大库清理计时:未执行(PR 亦声明未执行)。
  • 双模式目检:不适用(无 UI 改动)。

Intent alignment

  • 描述来源:PR 当前 body(fix 类模板,关联 #7091,明确排除可见消息/手动回退墓碑/新增索引),读取时间 2026-11-02;讨论 issue 无(signoff.held / discussionIssue 均空)。
  • 初始实现:6f9129b1fa409314d55b405002ecaaf1e4d7ba81(2026-10-29,含 discard 删除路径 + 后台清理 sweeper 全部主体,经 git show 实读该 revision 的 tx.ts 等)。后续 838e0db6b4(类型修正:clear-race 回调保持两参)、2966bcdfa7(收敛清理判据 + 有界窗口)。
  • 待合并 head:2966bcdfa7,与冻结 diff 一致。
原目标/初始行为 最终行为与影响 代码/提交证据 变化依据或待确认问题
派发前撤回的调度提示不再积墓碑(初始即删行) 不变:discard:true 同事务先软隐藏触发向量清理再删行 6f9129b1fa → head worker/opHandlers/tx.ts:messageRewindUserAfterClear 一致
历史清理:初始按 role='user' + rewind_at + origin.kind=scheduler 删所有隐藏行(含已送达后被手动回退的调度提示) 收敛为仅删可证明未送达的行(无 executionModel 且落单 rewind_at),手动回退墓碑保留 head tx.ts:messagePurgeRewoundSchedulerPrompts;greptile thread(tx.ts,已 Resolve)+ 2966bcdf 提交说明 bot P1 依据的缺陷事实成立(作者已核对 rewind.commit 盖戳行为);收敛是对原目标的保护性收窄,删除范围只减不增,非目标替换
初始全表按 rewind_at 顺序扫描 500 行窗口 + 游标,扫描量与墓碑数解耦,sweeper 单轮 5 批上限 rewoundSchedulerPromptCleanup.ts、2966bcdf 性能收窄,服务同一目标(本地库瘦身不拖慢本机)

结论:一致。两个已 Resolve 的 Codex/Greptile 意见(手动回退误删 P1、清理扫描量 P2)均在当前 head 实证落实(判据 SQL 与窗口游标均有回归测试锁定);未见删除/削弱原承诺、无关扩范围或以绕开验证消除报错的情形。

Overall

pass —— 没有 P0/P1。维护者确认门按 arch 语义 fallback 处理(见下),thread 全部 Resolve(2/2),CI 双源全绿(pr-design-basis / client-ci 均 success),机器人静默期已超时放行(copilot-pull-request-reviewer、chatgpt-codex-connector 对当前 head 未表态,其意见可能在合并后到达,由 post-merge-sweep 兜底)。

维护门 fallback 记录(arch:core-paths 348 行 ≥ 150):逐文件读完整 diff 后判为缺陷修复 + 配套测试(bugfix 类)——修复「反复撤回留下软隐藏行、本地库膨胀」这一已证实缺陷;删除判据收敛、不改规则语义、无 schema/migration/协议变化;按 auto.fallback.action=review 继续审查,不创建 hold。

@MagicLizi
MagicLizi merged commit 86dd2a2 into makecindy:main Nov 1, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

touches:core 改动碰到架构核心路径(review-pr 自动维护,仅展示)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants