Repository navigation
fix(scheduler): 派发前撤回的提示直接删除,并定期清掉历史软隐藏 - #7828
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
[Medium impact] The changes delete a narrow set of hidden, undelivered scheduler messages from the local database. 合并前需要收窄历史清理条件,避免永久删除用户手动回退留下的调度记录。 FindingsFix 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.
|
|
@elfcv 这个 PR 因 review conversation 未 Resolve,已等待约 48 小时,当前仍有 2 条未解决讨论。 如果已按意见修改并验证,请在对应讨论中说明处理结果,再点击 Resolve conversation;如果问题还未解决,请先处理。仅回复“已修复”不会关闭讨论。 全部解决后,下一轮自动审查会重新检查 PR;仍需满足代码审查、CI 和其他合并条件。 |
例行任务在送达模型前被撤回时,只写 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>
05692d2 to
2966bcd
Compare
MagicLizi
left a comment
There was a problem hiding this comment.
PR #7828 审查报告(auto / standard 档)
- PR: #7828
- 标题: fix(scheduler): 派发前撤回的提示直接删除,并定期清掉历史软隐藏
- head: 2966bcd(3 commits)
- base: d3e072f(main)
- diffHash: f293869384c72427a1c6c21b0d3303debb701c87930e83f6f2f75339c6843b93(冻结证据一致)
Findings
没有 P0/P1。
不阻断的 P2 级观察(按口径不发 thread,仅记录):
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」。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.mdP0/P1 口径:无红线、崩溃、跨平台失效或凭证泄露;数据删除是本修复目标本身且限定可证明未送达的行,不构成数据丢失。AGENTS.md安全底线与测试门槛:无凭证入仓;新增测试经真实生产入口(rewindPersistedUserMessageAfterClear、purgeRewoundSchedulerPrompts、sweeper)验证可观察结果,最小失败输入与 tier 已在 PR 描述说明;pnpm check:test-hygienePASS。- UI / DESIGN.md:不适用(
format.uiCodeFiles为空,无渲染层改动)。
固定问题逐条
- 单一目的(撤回即删 + 历史未送达软隐藏清理)与 Description、测试、风险段一致,无夹带(15 个文件全部服务该目的)。
- 共享符号读方核查:
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 路径统一执行,历史清理删行前引用已在隐藏时回收,不留悬挂引用。 - 权限/用户数据/回滚:删除判据三重收敛(scheduler 来源 + 无 executionModel + 落单 rewind_at),手动回退墓碑与已送达行均保留(测试逐一锁住);回滚为回退提交。
- 无新依赖、无危险写法、无数据外发;security 内容门 0 命中(standard 档不跑 security-scan.mjs)。
- 测试证据:见 Verification,本地实测通过。
- 无组织/个人流程塞入 Core。
- 无 UI 证据要求。
- 不涉 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)崩溃,是审查环境内存限制而非类型错误,未取得本地类型检查结论;类型正确性以 GitHubclient-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。
这次改了什么
摘要
例行任务在送达模型前被撤回时,提示已经写入 messages,但只标记 rewind_at。反复撤回会留下大量软隐藏行,把本地库撑到数百 MB,拖慢本机和远程列表。这次对已证实未送达的调度提示先软隐藏、再在同一事务删除;调度器启动后每 10 分钟按批清掉历史上已经隐藏、且能证明从未送达的 scheduler 行。可见消息、用户手动回退的历史和其他回退墓碑不动。
历史清理只删同时满足两条的行:
agent_meta.executionModel为空(派发时onModelDispatched会写入它),且同会话里没有别的行共用同一个rewind_at(rewind.commit给提示和其后所有消息盖同一个时间戳,手动回退不会留下落单墓碑)。每次清理沿idx_messages_rewind_at只扫一个 500 行窗口并返回游标,扫描量与库里普通墓碑的数量无关;没有新增索引或 migration。变更类型
fix缺陷修复test测试范围
scheduler-host/runner.ts相邻,与 fix(scheduler): 检查类定时任务默认复用同一会话 #8270 无交集,任意顺序都可以合,后合的由作者 rebase。UI 变化
怎么验证的
自动验证
最新 head
2966bcdfa7(已 rebase 到origin/main19dad9c225),独立 Linux aarch64:测试价值:
rewindPersistedUserMessageAfterClear、purgeRewoundSchedulerPrompts和调度器启动后的清理。cursor=1499且不删,第二窗口删掉窗口外的两条并返回null;一页删满limit时重扫同一窗口。sweeper 用例锁住游标跨批传递、扫完即停、下一轮归零、单轮 5 批上限。定向 ESLint:改动的 7 个文件里,
WorkerThreadTransport.ts第 401/419/1272 行与messagesClearRace.test.ts第 11 行有既有no-useless-escape/no-explicit-any报错,均不在本 PR 改动范围内,本轮没有新增 lint 错误,也不宣称全文件 lint 通过。手工验证
不涉及。没有导入 2GB 的真实伙伴库做清理计时。
未执行的验证
风险
风险分类
影响与回滚
提交前检查