fix: 清理供应商同步后的幽灵任务索引 / prune ghost tasks after provider sync#1485
fix: 清理供应商同步后的幽灵任务索引 / prune ghost tasks after provider sync#1485Yuimi-chaya wants to merge 3 commits into
Conversation
|
感谢贡献。这个问题确实存在,但当前实现涉及删除用户任务索引,仍有几个阻塞风险,暂不建议合并。
当前主分支的
如果 Codex 在计划和写入之间新增或重命名任务,新内容会被旧快照覆盖。建议:
当前逻辑只要某个合法 JSON 对象带非空 请只删除明确识别为当前已知 session-index schema 的记录,其余未知结构必须保留并补测试。
需要确认云端任务、尚未完整落盘的任务、延迟同步任务是否可能只存在于
两处恢复索引都使用 请传播或聚合恢复错误,并在失败结果中保留备份目录及明确警告。
请补充以下用例:
三平台 CI 虽然全绿,但删除判定和并发写入边界还没有被覆盖。修复以上阻塞项后再考虑合并。 |
|
感谢详细审查。指出的风险成立,我已在提交 1. 补全存活来源已纳入:
SQLite 路径发现也已对齐 #1438 的 2. 处理 Codex 运行时覆盖竞争普通供应商同步和启动前自动同步现在完全不会清理 清理被拆分为独立的“预览 → 用户确认 → 应用”流程。实际用户目录执行清理时,会在开始和原子替换前分别检查 Codex App / ChatGPT 进程;App 未完全退出时拒绝执行。 同时加入 SHA-256 快照校验和写入前逐字节复核。预览后若索引被新建、重命名或同步任务改动,本次清理会直接中止。 写入使用同目录临时文件原子替换。Windows 使用 3. 收紧已知 schema现在只有严格匹配 无法解析的行、缺少字段、包含额外字段以及其他未知结构都会原样保留。任务标题只用于预览,不参与删除判定。 4. 增加 dry-run、完整预览和显式确认普通同步永不删除索引。 管理器会先执行无副作用预览,完整展示全部候选记录及 thread ID,默认不勾选。用户可以逐项选择或全选,后端只接收实际勾选的 ID。 弹窗也明确提示候选项可能是云端或尚未落盘的任务,并要求清理前退出 Codex App / ChatGPT。 5. 消除静默回滚问题索引清理现在是独立的单文件原子操作,不再先覆盖索引后依赖静默回滚。 修改前会完整备份原始 6. 补充测试当前验证包括:
回归用例覆盖 PR 正文也已经按照当前实现重新整理。新一轮三平台 CI 正在运行,请继续审查。 |
|
感谢根据上一轮审查重构。自动删除已经移除,存活来源、严格 schema、预览确认、快照校验、备份和原子替换等主要风险目前都处理得比较完整。 二次审查仍发现一个阻塞边界:Windows 上的“Codex App / ChatGPT 必须完全退出”检查会漏掉便携版或非 WindowsApps 安装的 当前
代码注释还明确排除了普通独立 建议为删除流程增加更严格、独立的进程保护:清理期间宁可把任何受支持的 这个安全边界补齐后再考虑合并。 |
|
感谢指出。已在最新提交中增加独立于 Watcher 的删除流程进程保护:任何 WindowsApps 或便携版 同时将第二次进程检查移动到最终字节复核之后、原子替换之前。已补充覆盖 WindowsApps ChatGPT、便携 ChatGPT、Codex App 和 CLI 的 Windows 回归测试。 当前验证结果:
对应远端提交为 |
中文
2026-07-14 安全重构说明
根据维护者对删除边界的审查,本 PR 已撤销首版“在普通供应商同步中自动清理索引”的实现。现在供应商自动同步、启动前同步和普通
run_provider_sync都不会删除session_index.jsonl内容。清理改为独立的两阶段流程:管理器先只读预览候选项,用户逐项选择并明确确认后,后端才执行单文件原子清理。该流程要求 Codex App / ChatGPT 已完全退出;如果 App 仍在运行、预览后索引发生变化、确认列表过期或原子写入失败,本次清理会中止。
问题背景
供应商同步会统一 rollout 与 SQLite 中的
model_provider,但 Codex App 还可能保留已经失去会话来源的session_index.jsonl条目。重启后,这些孤立条目会重新显示在任务列表中,但无法打开或归档。实际复现时,用户曾为验证模型路由创建几条临时任务,例如
确认在线、回应问候和确认在线状态,并在测试结束后将它们删除。随后切换供应商并重启 Codex App,这些任务却以“仅剩标题”的条目重新出现在侧边栏。作为对照,另一个标题相近但仍在正常使用的任务
回复在吗必须保留。这个现象说明,清理不能依赖任务标题,而必须根据 thread ID 的实际数据来源判断。存活来源与 schema 边界
预览阶段会从以下来源联合收集 thread ID;任一来源存在即保留:
sessions和archived_sessions中可解析 rollout 的session_meta.payload.id。threads.id、local_thread_catalog.thread_id、automation_runs.thread_id、inbox_items.thread_id。sessions.id、messages.session_id、thread_dynamic_tools.thread_id、thread_goals.thread_id、thread_spawn_edges.parent_thread_id/child_thread_id、stage1_outputs.thread_id、agent_job_items.assigned_thread_id。#1438的local_thread_catalog,并覆盖仅含关联表的候选数据库。只有严格匹配当前已知三字段 schema(
id、thread_name、updated_at)的 JSON 对象才会进入候选列表。无法解析的行、缺字段记录、带额外字段的未来结构和其他未知对象全部原样保留。预览与显式确认
并发、原子写入与备份
MoveFileExW(MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH),其他平台使用同文件系统rename。session_index.jsonl到backups_state/provider-sync/...,并记录快照和清理数量。backupDir和明确恢复提示;不再使用静默忽略的索引回滚写入。验证
cargo test -q -p codex-plus-data --test provider_sync --test storage_adapter -j 2:22/22 + 18/18通过。cargo test -q -p codex-plus-core --test relay_config -j 2:95/95通过。cargo check -q -p codex-plus-manager -j 2。npm run check。node tools/i18n-verify.mjs:普通文本602/602、模板文本50/50。local_thread_catalog、automation/inbox、仅关联表数据库、损坏 rollout 文件名、未知带id结构、普通同步零删除、逐项确认、预览后并发修改、原子写失败保留原文件和暴露备份路径。English
Safety redesign on 2026-07-14
Following maintainer review of the deletion boundaries, this PR removes the initial behavior that automatically pruned the index during ordinary provider sync. Automatic provider sync, pre-launch sync, and normal
run_provider_synccalls never delete content fromsession_index.jsonlnow.Cleanup is a separate two-phase flow. The manager first performs a read-only preview; the backend applies a single-file atomic cleanup only after the user reviews and explicitly selects candidate entries. Codex App / ChatGPT must be fully exited. Cleanup aborts if the App is running, the index changes after preview, the confirmed selection becomes stale, or atomic replacement fails.
Background
Provider sync normalizes
model_provideracross rollout files and SQLite, while Codex App may still retainsession_index.jsonlentries whose underlying conversation sources have already disappeared. Those orphaned entries can return after restart but cannot be opened or archived.During reproduction, the user created and later deleted several temporary model-routing tasks, including
确认在线,回应问候, and确认在线状态. After switching providers and restarting Codex App, they returned as title-only sidebar entries.In contrast, the similarly named active task
回复在吗had to remain. Cleanup therefore cannot use titles and must rely on actual thread-ID sources.Live sources and schema boundary
Preview collects thread IDs from every source below. Any match preserves the entry:
session_meta.payload.idvalues insessionsandarchived_sessionsrollouts.threads.id,local_thread_catalog.thread_id,automation_runs.thread_id, andinbox_items.thread_id.sessions.id,messages.session_id,thread_dynamic_tools.thread_id,thread_goals.thread_id, both sides ofthread_spawn_edges,stage1_outputs.thread_id, andagent_job_items.assigned_thread_id.local_thread_catalogand also includes databases containing only related session tables.Only JSON objects that exactly match the currently known three-field schema (
id,thread_name, andupdated_at) can become candidates. Malformed lines, missing-field records, future records with extra fields, and all other unknown structures are preserved verbatim.Preview and explicit confirmation
Concurrency, atomic replacement, and backup
MoveFileExW(MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH); other platforms use same-filesystemrename.session_index.jsonlis backed up underbackups_state/provider-sync/...with the snapshot and selected count.backupDirin the command result with an explicit recovery warning. The previous silently ignored rollback writes no longer exist.Verification
cargo test -q -p codex-plus-data --test provider_sync --test storage_adapter -j 2:22/22 + 18/18passed.cargo test -q -p codex-plus-core --test relay_config -j 2:95/95passed.cargo check -q -p codex-plus-manager -j 2.npm run check.node tools/i18n-verify.mjs: plain602/602, template50/50.local_thread_catalog, automation/inbox, relation-only databases, damaged rollout filename fallback, unknown structures containingid, zero deletion during ordinary sync, explicit selection, concurrent index changes after preview, and atomic-write failure with original preservation and visible backup path.